Webhooks
Webhooks let Wabot push events to your server as they happen, so you don’t have to poll the API.
Add a webhook
Section titled “Add a webhook”- In Wabot, open Developer → Webhook and click Add New.
- Enter your Webhook URI. It must be a public HTTPS endpoint that accepts
POSTrequests with a JSON body. - Tick the Web Events you want to receive.
- If you selected WhatsApp Message Received, choose which message sub-types to send (text, image, button reply and so on).
- Save. You can add several webhooks, for example one per system.
Events
Section titled “Events”WhatsApp Message Received
Section titled “WhatsApp Message Received”Sent when a customer sends you a message.
{ "messageId": "wamid.HBgMOTE5NDAwMjk0MTEwFQIAEhgg…", "status": "received" }Use the message sub-types to choose which kinds of message trigger the event:
Text Message · Ad Message · Sticker Message · Image · Video · Document · Audio · Location Message · Contact Message · Simple Button Message (quick reply) · Radio Button Message (list reply) · Flow · Order · Unknown Message
To read the full message, call GET /v2/conversations/recent or the messages endpoint for that contact.
Template Status Updated
Section titled “Template Status Updated”Sent when Meta approves, rejects or pauses one of your templates.
{ "template_id": "1234567890123456", "status": "APPROVED" }Data View
Section titled “Data View”Sent when a workflow run finishes successfully. You can see the data it collected under Settings → Data View.
{ "masterWorkflowId": 42, "status": "Success" }Campaign Completed
Section titled “Campaign Completed”Sent for campaign updates.
{ "msg": "Campaign completed", "responseData": { "campaign_id": 77, "name": "Diwali offer" } }This event includes an X-Signature header. See Verify signatures.
Responding to webhooks
Section titled “Responding to webhooks”- Return any
2xxstatus quickly, within a few seconds. Do slow work in the background. - Wabot sends each event once and does not retry. If your endpoint is down, the event is lost, so for critical data, also reconcile periodically with the API.
- Expect duplicates and out-of-order events, and make your handler idempotent, for example by keying on
messageId.
app.post('/wabot/webhook', express.json(), (req, res) => { res.sendStatus(200); // acknowledge first queue.add('wabot-event', req.body); // process later});@app.post("/wabot/webhook")def wabot_webhook(): event = request.get_json() queue.enqueue(process_event, event) # process later return "", 200Verify signatures
Section titled “Verify signatures”Campaign Completed requests include an X-Signature header. It is a hex HMAC-SHA256 of the JSON-encoded msg value, using your Client Secret as the key:
import crypto from 'node:crypto';
function isFromWabot(req) { const expected = crypto .createHmac('sha256', process.env.WABOT_CLIENT_SECRET) .update(JSON.stringify(req.body.msg)) .digest('hex'); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.get('X-Signature') || ''));}The other events aren’t signed yet. Protect your endpoint with a hard-to-guess path, such as /wabot/webhook/7f3c…, and only accept requests over HTTPS.
Per-message callbacks
Section titled “Per-message callbacks”To track a specific message instead of every event, pass callback_url and event when you send it. Wabot then posts sent, delivered, read, failed and replied updates for that message only.
Inbound webhooks (start a workflow)
Section titled “Inbound webhooks (start a workflow)”Webhooks can also go the other way: your system calls Wabot to start a workflow. See Start a workflow from a webhook.
