Receiving messages
Get incoming messages, delivery receipts and template updates on your server.
Register a webhook URL in Settings → API & webhooks and choose which events it should receive. We send a POST request to your URL for each one, signed with your whsec_ secret in the X-BigRadar-Signature header so you can verify it's genuinely from us.
Events
| Field | Type | Required | Description |
|---|---|---|---|
| message.received | default | Required | A customer sent you a WhatsApp message. |
| message.failed | default | Required | An outbound message could not be delivered. Carries Meta's error code. |
| message.sent | opt-in | optional | Meta accepted an outbound message. High volume. |
| message.delivered | opt-in | optional | The message reached the recipient's device. High volume. |
| message.read | opt-in | optional | The recipient opened it. High volume. |
| template.status_updated | opt-in | optional | Meta approved, rejected or paused one of your templates. |
The three delivery statuses fire on every outbound message, so a campaign to 10,000 contacts produces 30,000 deliveries. They are opt-in for that reason — enable them only if you record read receipts.
Envelope
Every event shares the same shape: an id, the event name, a createdAt timestamp and an event-specific data object.
{
"id": "evt_...",
"event": "message.received",
"createdAt": "2026-06-25T10:00:00Z",
"data": {
"conversationId": "a1b2c3d4-...",
"contact": { "phone": "919876543210", "name": "Mohit" },
"message": {
"type": "text",
"text": "Hi there",
"waMessageId": "wamid....",
"timestamp": "1719312000"
}
}
}Delivery receipts
message.sent, message.delivered and message.read share one shape. template is filled in when the message came from a template, so you can reconcile a receipt against your own send.
{
"id": "evt_...",
"event": "message.delivered",
"createdAt": "2026-06-25T10:00:02Z",
"data": {
"waMessageId": "wamid....",
"recipient": "919876543210",
"status": "delivered",
"timestamp": "1719312002",
"conversationId": "65868fbf9995a3bf...",
"pricing": { "billable": true, "model": "PMP", "category": "utility" },
"template": { "name": "order_shipped", "campaignId": null },
"error": null
}
}Failures
{
"id": "evt_...",
"event": "message.failed",
"createdAt": "2026-06-25T10:00:03Z",
"data": {
"waMessageId": "wamid....",
"recipient": "919876543210",
"status": "failed",
"error": {
"code": 131047,
"title": "Re-engagement message",
"message": "More than 24 hours have passed since the last reply."
}
}
}Template status changes
Sent when Meta approves, rejects or pauses a template — so you learn about a rejection without polling or opening the dashboard.
{
"id": "evt_...",
"event": "template.status_updated",
"createdAt": "2026-06-25T11:00:00Z",
"data": {
"name": "order_shipped",
"language": "en",
"status": "REJECTED",
"previousStatus": null,
"reason": "INVALID_FORMAT",
"templateId": "1234567890"
}
}Signature header
X-BigRadar-Signature: t=1719312000,v1=9f3a7c21e8b4...Verify by computing HMAC_SHA256(secret, t.rawBody) and comparing it to the v1 value. Compare the two digests with a constant-time function, and reject a request whose t is more than a few minutes old — that is what stops a captured request being replayed later.
Retries and duplicates
Reply 2xx as soon as you have stored the event; do your processing afterwards. A non-2xx or a timeout past 10 seconds is retried up to 4 times over roughly 36 minutes, after which the delivery is marked failed and can be replayed from the dashboard. An endpoint that fails repeatedly is disabled automatically.
Deduplicate on data.waMessageId together with the event name rather than on id — id is unique per delivery attempt group, while the message id is what identifies the underlying message across retries.