whatsapp webhook as each message you sent moves through its lifecycle. This guide covers the events, how to verify them, and how to build a robust receiver. To register and manage webhooks (create, secret, test, deliveries, retries), see the Karibu Webhooks API; for full event payloads, see the WhatsApp reference -> Webhook events.
Prerequisites
Two things must both hold to receive events:- You have a registered
whatsappwebhook for the app (one webhook per app perservice_type). Register it viaPOST /v1/webhooks/withservice_type: "whatsapp". - The message was sent via the Developer API. Only API-originated messages are tagged with your API key and correlated back to your app. Messages sent from the Briq dashboard do not notify your webhook.
The four events
| Event | Meaning |
|---|---|
whatsapp.sent | The message left Briq and was accepted by the provider. |
whatsapp.delivered | The message reached the recipient’s device. |
whatsapp.read | The recipient opened/read the message. |
whatsapp.failed | The message could not be delivered (terminal - adds error_code / error_message). |
sent -> delivered -> read. failed is an alternate terminal state. Briq enforces a status-rank guard so a message never regresses (a late sent cannot overwrite delivered); not every message produces all four events.
Event payload
Every event shares the same envelope:event_id- unique per event (wa_+ 32-char hex). Use it as your idempotency key.data.message_id- the same Briq conversation-message UUID returned by your send call. Use it to correlate the event with the message in your system.- On
whatsapp.failed,dataalso carrieserror_codeanderror_message.
Verifying the signature
Every delivery carries anX-Briq-Signature header: HMAC-SHA256 of the raw request body, keyed by your webhook’s signing secret. Fetch the secret once, store it securely, and verify on every request.
- Compute the HMAC over the raw request bytes - do not re-serialize parsed JSON (key ordering/whitespace differences break the signature).
- Use a constant-time comparison.
- Reject if the signature is missing or doesn’t match.
Idempotency & ordering
Delivery is at-least-once and not strictly ordered. Design your handler accordingly:- Idempotency: dedupe on
event_id. If you’ve already processed one, return2xxand skip - retries reuse the sameevent_id. - Ordering: events may arrive out of order or be redelivered. Don’t assume
sentprecedesdelivered. - Never regress status on your side: once you record a message as
read, don’t downgrade it because a latedeliveredarrived; oncefailed, treat it as terminal. - Key off
data.message_idto attach each event to the right message.
What your endpoint must return
| Your response | Briq behavior |
|---|---|
| Any 2xx | Success; Briq stops retrying this attempt. |
| Non-2xx / timeout / connection error | Failure; retried (up to 3 attempts) with backoff. |
2xx (e.g. 204 No Content), then process asynchronously so your endpoint isn’t marked as failing under load.
Inspecting and replaying deliveries
Use the Webhook Management endpoints to audit delivery health:GET /v1/webhooks/{webhook_id}/deliveries- list attempts and statuses.GET /v1/webhooks/{webhook_id}/stats- aggregate success/failure counts.GET /v1/webhooks/deliveries/{delivery_id}- full payload snapshot for one delivery.POST /v1/webhooks/deliveries/{delivery_id}/retry- manually re-attempt a failed delivery.
Upcoming
- Inbound customer messages (
whatsapp.received) are not yet delivered to webhooks - poll the Conversations API meanwhile. - The test event (
POST /v1/webhooks/{id}/test) currently emits a syntheticsms.sentpayload regardless ofservice_type; a nativewhatsapp.*test event is upcoming. It still verifies connectivity and signature handling.
Related: Sending messages | Senders & conversations | Webhooks (subscriber guide)