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.
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;
}
Delivery & retries
- Return a
2xxstatus quickly to acknowledge. Do the real work asynchronously — long handlers cause timeouts and retries. - If your endpoint errors or times out, AtomMatrix retries with exponential backoff for up to 24 hours.
- Delivery is at-least-once, so the same event may arrive more than once. De-duplicate on the event
idand make your handler idempotent. - Order isn't guaranteed. Use
created_atand resource status rather than assuming arrival order.
Event catalog
| Event | When it fires |
|---|---|
message.updated | An outbound message reached a new delivery status. |
message.received | An inbound message arrived on one of your numbers. |
call.initiated / call.ringing / call.answered / call.completed | Call lifecycle transitions. |
call.recording.ready | A call recording finished processing. |
verification.approved / verification.expired | An OTP verification reached a terminal state. |
email.delivered / email.opened / email.clicked / email.bounced / email.complained | Email delivery and engagement. |
conversation.message / run.completed / run.requires_action / conversation.handoff | AI agent activity. |
knowledge.source.ready / knowledge.source.failed | A knowledge source finished indexing, or failed to sync. |