Webhooks
Receive conversation events on an HTTPS endpoint.
Webhooks notify your server when an AI or human message is stored on a public API conversation, and when the agent requests a human handoff.
Create and delete endpoints with the Webhooks API. The signing secret is returned only on create — store it immediately.
These events are delivered only to endpoints you create through this API. They are not sent to Zapier, Make, or n8n dashboard webhooks.
Events
| Event | When it fires |
|---|---|
public_api_message_created | After an AI reply is stored on a public API conversation, and after every human reply on a public API conversation (API or dashboard). It does not fire for visitor sends. |
public_api_human_handoff | When the AI requests human support on a public API conversation. |
Widget and email threads do not trigger these events. A dashboard human reply on a public API thread still notifies your endpoint.
Deliveries are queued. Failed deliveries are retried up to three times (after 60 seconds,
5 minutes, and 15 minutes). Respond with 2xx to acknowledge.
Payloads
Every delivery is a JSON POST. Top-level id is the conversation UUID.
public_api_message_created
{
"id": "3f1a9c2e-8b4d-4e6a-9c1f-2a3b4c5d6e7f",
"event": "public_api_message_created",
"timestamp": "2026-09-05T10:16:05+00:00",
"data": {
"conversation": { "id": "3f1a9c2e-8b4d-4e6a-9c1f-2a3b4c5d6e7f" },
"message": {
"id": "a1b2c3d4-e5f6-4708-9a1b-2c3d4e5f6071",
"role": "assistant",
"content": "Here is the AI reply."
}
}
}role is assistant for AI replies and human for human replies. It is never visitor for
this event.
public_api_human_handoff
{
"id": "3f1a9c2e-8b4d-4e6a-9c1f-2a3b4c5d6e7f",
"event": "public_api_human_handoff",
"timestamp": "2026-09-05T10:17:00+00:00",
"data": {
"conversation": { "id": "3f1a9c2e-8b4d-4e6a-9c1f-2a3b4c5d6e7f" }
}
}Verifying deliveries
Each request includes:
| Header | Meaning |
|---|---|
X-Webhook-Signature | Hex HMAC‑SHA256 of the raw JSON body, keyed with the endpoint secret. |
X-Webhook-Event | public_api_message_created or public_api_human_handoff. |
X-Webhook-Delivery-ID | Delivery identifier. |
User-Agent | AssistLoop-Webhooks/1.0. |
Compute HMAC-SHA256(secret, raw_body) and compare it to X-Webhook-Signature with a
constant‑time equals. Verify against the bytes you received — do not re‑serialize the parsed
JSON.
import crypto from 'node:crypto';
function isValidAssistLoopWebhook(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const a = Buffer.from(signatureHeader, 'utf8');
const b = Buffer.from(expected, 'utf8');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}The callback URL must be https:// and must not point at localhost or an internal IP.