WhatsApp webhook guide
How WhatsApp Business API webhooks work, how to verify them, and how to handle incoming messages and delivery updates.
What a webhook is
A webhook is a URL on your server that Meta calls whenever something happens on your WhatsApp number, such as a new message from a customer or a delivery status change. Instead of you polling for updates, Meta pushes them to you in real time.
You set the callback URL and a verify token in your app settings, then subscribe to the fields you care about, usually messages.
Step 1: Verify your endpoint
When you save your callback URL, Meta sends a GET request with three query parameters. Echo back the challenge only if the token matches the one you configured.
// GET /webhook?hub.mode=subscribe&hub.verify_token=...&hub.challenge=...
function handleVerify(req, res) {
const mode = req.query["hub.mode"];
const token = req.query["hub.verify_token"];
const challenge = req.query["hub.challenge"];
if (mode === "subscribe" && token === MY_VERIFY_TOKEN) {
return res.status(200).send(challenge);
}
return res.sendStatus(403);
}Step 2: Receive events
Once verified, Meta sends event data as a POST with a JSON body. Incoming messages arrive under entry[].changes[].value.messages.
{
"object": "whatsapp_business_account",
"entry": [{
"changes": [{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"contacts": [{ "wa_id": "9198XXXXXXXX" }],
"messages": [{
"from": "9198XXXXXXXX",
"id": "wamid.XXXX",
"timestamp": "1718000000",
"type": "text",
"text": { "body": "Hi, is this available?" }
}]
}
}]
}]
}Delivery and read receipts arrive the same way under a statuses array, with values like sent, delivered, read, and failed.
Step 3: Verify the signature
Every POST includes an X-Hub-Signature-256 header. It's an HMAC SHA-256 of the raw request body using your app secret. Check it against the raw body before trusting the payload.
import crypto from "crypto";
function isValid(rawBody, signatureHeader) {
const expected =
"sha256=" +
crypto.createHmac("sha256", APP_SECRET)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader),
);
}Handling events well
Respond with a 200 quickly, even before you finish processing. Meta retries if you're slow or return an error, which can create duplicates.
Do the real work in the background, and deduplicate on the message id since the same event can arrive more than once. Always verify the signature before acting on a payload.
Skip the plumbing
With Boldally Chat you don’t build or run any of this. Messages land in a shared inbox, delivery and read status show on each message, and campaign analytics update on their own. The webhook details above are here for teams building directly on the API.
Payload shapes and headers are defined by Meta and can change. Check Meta’s official WhatsApp Cloud API webhook documentation for the current spec.
Put your WhatsApp to work.
Free for 14 days. No credit card, and no setup call to sit through.