Skip to main content
Briq pushes delivery status events to your registered 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:
  1. You have a registered whatsapp webhook for the app (one webhook per app per service_type). Register it via POST /v1/webhooks/ with service_type: "whatsapp".
  2. 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.
# Register, then fetch the signing secret.
curl -X POST "https://karibu.briq.tz/v1/webhooks/" \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{ "app_id": "YOUR_APP_ID", "service_type": "whatsapp", "url": "https://api.yourdomain.com/briq/webhooks/whatsapp" }'

curl "https://karibu.briq.tz/v1/webhooks/WEBHOOK_ID/secret" -H "X-API-Key: YOUR_API_KEY"

The four events

EventMeaning
whatsapp.sentThe message left Briq and was accepted by the provider.
whatsapp.deliveredThe message reached the recipient’s device.
whatsapp.readThe recipient opened/read the message.
whatsapp.failedThe message could not be delivered (terminal - adds error_code / error_message).
The happy path advances 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": "whatsapp.delivered",
  "event_id": "wa_a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "app_id": "8a1f0c4e-2b3d-4e5f-9a8b-7c6d5e4f3a2b",
  "service_type": "whatsapp",
  "timestamp": "2026-05-30T11:02:19.880142+00:00",
  "data": {
    "message_id": "d6b1f0a2-7c3e-4d5a-9b8c-1e2f3a4b5c6d",
    "conversation_id": "b2c3d4e5-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "provider_message_id": "wamid.HBgLMjU1...",
    "recipient": "+255700000000"
  }
}
Key fields:
  • 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, data also carries error_code and error_message.

Verifying the signature

Every delivery carries an X-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.
import hashlib, hmac
from flask import request, abort

WEBHOOK_SECRET = "whsec_..."

def verify(raw_body: bytes, signature_header: str) -> bool:
    expected = hmac.new(WEBHOOK_SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header or "")

@app.post("/briq/webhooks/whatsapp")
def handle():
    raw = request.get_data()  # RAW bytes, not request.json
    if not verify(raw, request.headers.get("X-Briq-Signature", "")):
        abort(401)
    event = request.get_json()
    # ... dedupe on event["event_id"], then process ...
    return "", 204

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, return 2xx and skip - retries reuse the same event_id.
  • Ordering: events may arrive out of order or be redelivered. Don’t assume sent precedes delivered.
  • Never regress status on your side: once you record a message as read, don’t downgrade it because a late delivered arrived; once failed, treat it as terminal.
  • Key off data.message_id to attach each event to the right message.

What your endpoint must return

Your responseBriq behavior
Any 2xxSuccess; Briq stops retrying this attempt.
Non-2xx / timeout / connection errorFailure; retried (up to 3 attempts) with backoff.
Acknowledge quickly with a 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 synthetic sms.sent payload regardless of service_type; a native whatsapp.* test event is upcoming. It still verifies connectivity and signature handling.

Related: Sending messages | Senders & conversations | Webhooks (subscriber guide)