Webhooks
Events, payloads, signatures and retries.
Webhooks push events from your workspace to any HTTPS endpoint. Create them under Settings → Webhooks, choose which events to receive, and use the signing secret to verify requests.
Events #
| Event | Fires when |
|---|---|
conversation.created | A new conversation starts on any channel. |
conversation.closed | A conversation is marked closed by a person or by the AI. |
message.created | A visitor, AI or agent message is saved (internal notes excluded). |
lead.created | A lead is captured via form, email capture or the capture_lead tool. |
escalation.created | An escalation rule fires or the agent hands off to a human. |
action.failed | A custom action returned a non-2xx or timed out. |
Payload #
json
{
"id": "evt_5f1c…",
"type": "lead.created",
"createdAt": "2026-09-16T10:12:03.000Z",
"data": {
"chatbot": {
"uuid": "0b0e…",
"name": "Acme Support"
},
"lead": {
"id": 88,
"conversationId": 4021,
"name": "Sam",
"email": "sam@example.com",
"phone": null,
"fields": {
"budget": "£5k"
},
"source": "form"
}
}
}Headers on every delivery:
| Header | Value |
|---|---|
X-SupportAi-Event | The event type. |
X-SupportAi-Delivery | Unique delivery id — use it for idempotency. |
X-SupportAi-Signature | t=<unix seconds>,v1=<hex hmac> |
Verifying signatures #
Compute HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and compare it to v1 in constant time. Reject timestamps older than five minutes to prevent replays.
js
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(parts.v1, "hex"), Buffer.from(expected, "hex"));
}Use the raw body
Verify against the exact bytes received, before any JSON parsing or re-serialisation. In Express use
express.raw() for the webhook route.Retries #
We expect a 2xx within 10 seconds. Anything else is retried with exponential backoff (30s, 1m, 2m, 4m, 8m, capped at 6h) for up to six attempts, then marked failed. You can inspect every delivery, see the response code, and replay from the dashboard.