Guide

Webhooks

Get new messages and changes posted to your server as they happen.

Add an endpoint in Developers → Webhooks, or from your app with POST /api/v1/webhooks (scope webhooks:write). We POST each event to it as JSON.

EventWhen
message.createdA customer wrote, or a message was sent
message.updatedA message was delivered, read or failed
conversation.updatedStatus, assignee, tags or the AI switch changed
channel.updatedA channel connected or needs reconnecting
template.updatedMeta approved, rejected or paused a WhatsApp template (data.template.status)
campaign.updatedA campaign started, finished, was canceled or stopped (data.campaign.status, data.campaign.stats)
conversation.handoffThe AI agent passed a chat to the team (data.reason says why, data.conversation is the chat)
knowledge.updatedA knowledge source finished indexing (data.source.status is ready or failed)
Delivery body
{  "id": "evt_8f2c...",  "type": "message.created",  "createdAt": "2026-10-15T12:00:00.000Z",  "workspaceId": "b5d3d605-...",  "data": { "message": { "id": "...", "direction": "inbound", "body": "Hi!" } }}

Check the signature

Every delivery has an X-Inbox-Signature: t=…,v1=… header. v1 is the HMAC-SHA256 of <t>.<raw body>, keyed with the endpoint's secret. Try it:

Signature playgroundRuns in your browser; nothing is sent.
Expected X-Inbox-Signature
t=1792244905,v1=
import { createHmac, timingSafeEqual } from "node:crypto"; function verify(rawBody, header, secret) {  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");  return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));}

Retries

Answer with any 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 8 hours. Deliveries can repeat, so skip event ids you've already handled.