Skip to content

Signed webhooks

Instead of polling the API in a loop, let WhatSetter push events to you the moment they happen.

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
Terminal window
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.

Every delivery is signed HMAC-SHA256 of the raw body:

X-Whatsetter-Event: booking.created
X-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));
}
  • At least once: after a network incident, the same event may arrive twice. Deduplicate on the payload’s event_id.
  • Answer 2xx quickly (do your processing asynchronously); any other code counts as a failure.