Skip to main content

Webhooks

You don't configure any callback on Meta. We receive every event from the connected accounts, validate Meta's signature and redeliver to the endpoints you register.

The delivery body​

It's Meta's envelope, byte for byte. Handlers already written for the Cloud API work unchanged.

{
"object": "whatsapp_business_account",
"entry": [
{
"id": "111222333444555",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5541987111527",
"phone_number_id": "106540352242922"
},
"messages": [
{
"from": "5541999999999",
"id": "wamid.HBgNNTU0MTk5OTk5OTk5OQ...",
"timestamp": "1785312000",
"type": "text",
"text": { "body": "Hi, did my order ship?" }
}
]
}
}
]
}
]
}

Headers on every delivery​

HeaderContents
X-Joinotify-Signature-256sha256=<hex> — the delivery's HMAC
X-Joinotify-TimestampUnix seconds, included in the HMAC
X-Joinotify-EventEvent name, e.g. messages
X-Joinotify-DeliveryDelivery ID, stable across retries
User-AgentJoinotify-Webhook/1.0

Three rules that prevent most problems​

Answer 2xx first, process afterwards. The timeout is 20 seconds. A handler that writes to the database, calls three APIs and only then responds will blow up in production.

Validate the signature against the raw body. If you let the framework parse the JSON first, the signature will never match.

Be idempotent. A delivery can repeat. Deduplicate by X-Joinotify-Delivery or by the message wamid.

Where to go next​