Skip to content

Webhooks

Subscribing to events, verifying signatures, and surviving retries.

2 min read

Rather than polling for changes, subscribe an endpoint to events. Settings → Integrations → Webhooks.

Events

EventFires when
lead.createdA lead is created by any route
lead.status_changedA lead moves stage
lead.assignedOwnership changes
call.loggedA call is recorded
message.receivedAn inbound message arrives
deal.wonA deal reaches a won stage
deal.lostA deal reaches a lost stage
callback.missedA scheduled callback slot passes untouched

Payload

json
{
  "event": "lead.created",
  "event_id": "01KYM97KCJK416C465Y2A8AN3N",
  "occurred_at": "2026-07-29T11:42:03.512Z",
  "data": { "object": "lead", "public_id": "01H8XGJWBWBAQ4S1PT7C2N3M4K" }
}

The payload carries identifiers, not a full record. Fetch the object if you need its current state — by the time your handler runs, the copy in a webhook body may already be stale.

Verifying the signature

Every delivery carries X-Nexus-Signature: an HMAC-SHA256 of the raw request body, keyed with the endpoint's signing secret.

js
const expected = crypto
  .createHmac('sha256', process.env.NEXUS_WEBHOOK_SECRET)
  .update(rawBody)            // the raw bytes — NOT a re-serialised object
  .digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) {
  return res.status(400).end();
}
Warning
Verify against the raw body. Re-serialising the parsed JSON changes key order and whitespace, so the signature will never match. This is the single most common webhook integration failure, and it looks exactly like a wrong secret.

Use a constant-time comparison. A plain === on a signature leaks, one byte at a time, how much of a guess was correct.

Retries and idempotency

A non-2xx response is retried with exponential backoff for up to 24 hours.

Deduplicate on event_id. A retry sent after your handler succeeded but timed out will deliver the same event again, and processing it twice is a problem only your side can prevent — we cannot know whether your work completed.

Return 200 as soon as you have durably recorded the event, then do the work afterwards. A handler that spends thirty seconds processing before responding will be retried while it is still running.

The delivery log

Settings → Integrations → Webhooks → [endpoint] → Deliveries shows every attempt with its response code and body, so a failing endpoint is visible rather than silently dropping events. An endpoint that fails for 24 hours is disabled and the workspace owners are emailed.

Stuck on a response you did not expect? Send us the request_id from the error body and we can trace the exact call — contact support.

Webhooks — API reference — atomcrm.ai