The documentation is written in English and Dutch. You are reading the English version.
Webhooks
Each new lead and ticket sent to an address of yours as it happens, signed, and how your server checks it came from us.
A webhook sends each new lead and ticket to an address of yours the moment it arrives, so that another system, a CRM, a helpdesk or an automation of your own, can take it from there. Webhooks are part of Studio and up.
Note. Webhooks are being tested and are not open to every workspace yet. Where they are not, the Webhooks card does not appear in Settings, and the API answers
integrations_not_in_plan.
Adding a webhook
In Settings, on the Webhooks card, press New webhook and fill in:
- Address: where each event is sent. It has to start with
https://, have no port number and no user name or password in it, be at most 2,000 characters, and be reachable from the internet. An address whose name leads to a private network is refused, and the name is looked up again before every delivery. - New lead and New ticket: which events to send. At least one.
- Which agents: All agents, which includes agents you add later, or one agent, to send only its leads and tickets.
Press Create webhook. The next screen shows the signing secret, which your server checks each delivery with. It is shown this once. If you lose it, delete the webhook and add it again. Press I have copied it when you have.
A workspace has at most 10 webhooks, and an address once. Each one is listed with its address, its events, its agents and how the last delivery went.
What is sent
Each event is a POST with a JSON body:
{
"id": "evt_4b1c9e2f7a8d4e0b9c3f",
"type": "lead.created",
"createdAt": "2026-09-25T10:15:00.000Z",
"agentId": "ag_9f2c4e1b7d3a4c8e8b6f",
"data": {
"id": "lead_2e8a6c4b9d1f4a7e8c3b",
"name": "Sam Jansen",
"email": "sam@example.com"
}
}
type is lead.created or ticket.created. data is the lead or the ticket exactly as the API returns it, every field included; the example above is shortened. The fields are described under Leads and Tickets in the API reference.
Each request carries these headers:
| Header | What it holds |
|---|---|
content-type | application/json |
chatterlab-event | The event's type, as in the body |
chatterlab-delivery | The delivery's id: the same on every attempt of it, so a repeat can be recognised |
chatterlab-signature | t= the time of the attempt in Unix seconds, and v1= the signature |
user-agent | ChatterLab-Webhooks/1.0 |
Checking the signature
The signature is an HMAC-SHA256, in hexadecimal, of the time from t=, a full stop and the body exactly as it arrived, with the signing secret as the key. Check it before you trust a delivery: anyone who knows your address can post to it, but only we have the secret. In Node.js:
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the body as it arrived, before it is parsed as JSON.
function fromChatterLab(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map((part) => part.split("=")));
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return (
fresh &&
typeof parts.v1 === "string" &&
parts.v1.length === expected.length &&
timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))
);
}
Compare in constant time, as timingSafeEqual does, and refuse a delivery whose time is more than a few minutes from yours, so that a recorded one cannot be sent again later.
Answering, and when it fails
Answer with any 2xx status within 5 seconds. What your server answers besides the status is not read. A redirect is not followed and counts as a failure. A 410 says the address is gone for good, as Zapier answers for a Zap that was deleted: the webhook is deleted at once, without retries.
A delivery that fails is tried again after 5 minutes, 15 minutes, 1 hour, 3 hours, 6 hours and 12 hours: 7 attempts over almost a day. Each retry can come a few minutes later than that. After the last attempt fails, the webhook is switched off and the workspace owner is e-mailed. Nothing more is sent to it, and the leads and tickets of that day are not sent later: you find them in the dashboard. Once your server answers again, press Switch on again beside the webhook in Settings.
Events can arrive out of order, and now and then twice, for instance when your server did the work but answered too late. Use chatterlab-delivery to recognise one you have handled already.
Deliveries hold people's details, so each one is deleted 7 days after it was made, once it is delivered or given up.
Over the API
Webhooks can be added and deleted with an API key as well: GET /webhooks, POST /webhooks, GET /webhooks/{id} and DELETE /webhooks/{id}, described in the API reference. Switching one on again is done in the dashboard only. The MCP server has no tools for webhooks: where people's details are sent is not an assistant's to change.