PLATFORM

Webhooks

Most of what happens on the platform happens after your API call returns — a message gets delivered, a call completes, a customer replies. Webhooks push those events to an HTTPS endpoint you own, in near real time.

Subscribe to events

Register an endpoint in the console under Settings → Webhooks, or via the API. Choose which events to receive; subscribe only to what you use.

POST/v1/webhook_endpoints
curl https://api.atommatrix.ai/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.yourco.com/hooks/atommatrix",
    "events": ["message.updated", "message.received", "call.completed"]
  }'

The response includes a signing_secret (prefixed whsec_). Store it — you'll use it to verify every delivery. You can also subscribe to * to receive all events.

Event envelope

Every webhook is an HTTP POST with a JSON body in the same envelope: a unique event id, a type, a created_at, and a data object holding the resource.

{
  "id": "evt_01HN9Y3Z...",
  "object": "event",
  "type": "message.updated",
  "created_at": "2026-07-07T12:00:05Z",
  "data": {
    "id": "msg_01HN9K7QophZ8",
    "object": "message",
    "status": "delivered"
  }
}

Verify the signature

Every request carries an Atom-Signature header so you can confirm it really came from AtomMatrix and wasn't tampered with. The header contains a timestamp and an HMAC-SHA256 signature computed over timestamp + "." + raw_request_body using your endpoint's signing secret.

Atom-Signature: t=1782475205,v1=4f8a1d...c9

Verify it before trusting the payload. Compute the expected signature and compare in constant time; reject anything with a timestamp older than ~5 minutes to prevent replay.

Node.js

import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
  const signed = `${parts.t}.${rawBody}`;
  const expected = crypto.createHmac("sha256", secret).update(signed).digest("hex");
  const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return ok && fresh;
}
Verify against the raw body. Compute the HMAC on the exact bytes you received, before any JSON parsing or reserialization — otherwise whitespace or key ordering will change the signature and verification will fail.

Delivery & retries

Event catalog

EventWhen it fires
message.updatedAn outbound message reached a new delivery status.
message.receivedAn inbound message arrived on one of your numbers.
call.initiated / call.ringing / call.answered / call.completedCall lifecycle transitions.
call.recording.readyA call recording finished processing.
verification.approved / verification.expiredAn OTP verification reached a terminal state.
email.delivered / email.opened / email.clicked / email.bounced / email.complainedEmail delivery and engagement.
conversation.message / run.completed / run.requires_action / conversation.handoffAI agent activity.
knowledge.source.ready / knowledge.source.failedA knowledge source finished indexing, or failed to sync.