Signed webhooks
Instead of polling the API in a loop, let WhatSetter push events to you the moment they happen.
The events
Section titled “The events”| Event | Fired when |
|---|---|
contact.qualified |
The agent qualifies a lead (your prompt’s criteria) |
contact.not_qualified |
The agent rules a lead out |
booking.created |
A meeting is booked in the conversation |
message.received |
A lead replies |
agent.disconnected |
A WhatsApp number disconnects |
Subscribing
Section titled “Subscribing”curl -X POST "https://app.whatsetter.com/api/v1/webhooks" \ -H "Authorization: Bearer ws_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"url":"https://your-server.com/webhooks/whatsetter","events":["contact.qualified","booking.created"]}'The response contains the signing secret (whsec_…). Shown only once,
store it immediately. HTTPS endpoints only.
Test your receiver anytime:
POST /v1/webhooks/{id}/test sends you a signed event with
"is_test": true.
Verifying the signature
Section titled “Verifying the signature”Every delivery is signed HMAC-SHA256 of the raw body:
X-Whatsetter-Event: booking.createdX-Whatsetter-Delivery: <unique id>X-Whatsetter-Signature: sha256=<hex>import crypto from 'node:crypto';
function verify(rawBody, header, secret) { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(rawBody) // the RAW body, before any JSON.parse .digest('hex'); return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));}Delivery
Section titled “Delivery”- At least once: after a network incident, the same event may arrive
twice. Deduplicate on the payload’s
event_id. - Answer
2xxquickly (do your processing asynchronously); any other code counts as a failure.

