Webhook integration (subscriber guide)
This page describes what your HTTPS endpoint receives from Briq when SMS webhooks are enabled for your app. It does not assume access to Briq’s internal systems or source code.Overview
Briq sends asynchronous HTTP callbacks when SMS-related events occur—for example, after a provider accepts a message for routing, or when a delivery report updates final status. Each callback is an HTTP POST to the URL you registered, with a JSON body (the event envelope). Callbacks are sent only when:- The SMS was sent using credentials linked to your developer app, and
- You have configured a webhook for that app and channel (for SMS, the channel is
sms).
Request shape
| Aspect | Value |
|---|---|
| Method | POST |
| Body | JSON (see JSON envelope (top-level fields)) |
Content-Type | application/json |
Outbound headers
Briq sends the following headers on each delivery attempt:| Header | Always present | Purpose |
|---|---|---|
Content-Type | Yes | application/json |
User-Agent | Yes | Briq-Webhook/1.0 |
X-Briq-App-ID | Yes | UUID of your Briq developer app |
X-Briq-Service-Type | Yes | Channel, e.g. sms |
X-Briq-Signature | Only when a signing secret is configured | sha256=<hex> — HMAC-SHA256 over the raw request body (see Verifying signatures) |
X-Briq-Signature is omitted.
JSON envelope (top-level fields)
The body is one JSON object with stable key ordering and no extra whitespace between tokens (compact JSON). Briq signs exactly those bytes.| Field | Type | Description |
|---|---|---|
id | string | Unique id for this event (Briq uses an evt_-prefixed identifier) |
event | string | Event name, e.g. sms.sent, sms.delivered |
channel | string | Lowercase channel, e.g. sms |
app_id | string | Same app UUID as in X-Briq-App-ID |
created_at | string | UTC timestamp, ISO-8601 with milliseconds and Z |
data | object | Event-specific fields (see SMS events) |
Verifying signatures
Secret
Use the webhook signing secret shown to you when you create or rotate the webhook in the Briq developer console (or that your Briq operator provisioned for you). Store it securely (secret manager, environment variable, never in client-side code).Algorithm
- Take the raw request body as bytes (use your framework’s raw body buffer, not a pretty-printed string).
- Read
X-Briq-Signature. It has the formsha256=<lowercase_hex>. - Compute
HMAC-SHA256(secret_as_utf8_bytes, raw_body_bytes)and hex-encode the result. - Compare your hex string to the value after
sha256=using a constant-time comparison.
JSON.stringify / json.dumps unless you can reproduce Briq’s exact canonical JSON; prefer raw body only.
Edge cases
| Situation | What to do |
|---|---|
| Secret configured | Require X-Briq-Signature and verify it |
| No secret configured | Signature header is absent; you cannot verify with HMAC—decide whether to accept only signed webhooks in your environment |
| Bad signature | Respond with 4xx (recommended) or any non-2xx; Briq will retry according to its retry policy |
Example (Python)
Example (Node.js)
Idempotency and event id
The same logical event may be delivered more than once if your server returns an error, times out, or Briq retries after a network failure. The envelope id is stable for that delivery attempt’s payload.
Recommendation: Deduplicate using id, or a composite such as id + event + data.message_id. For application correlation between your send handler and webhook processing, use data.job_id (and data.recipient or data.message_id when one instant send has multiple recipients).
SMS events
Correlating webhooks with the send response
When you callPOST /v1/message/send-instant (or another send endpoint that returns a job id), a successful response includes a job_id. Store it for the in-flight operation.
Webhook payloads include data.job_id for submit-time events (sms.sent, submit-time sms.failed). That value is the same as the job_id returned by the send API for that job, so you can match asynchronous callbacks to the HTTP request that queued the send.
For batch instant sends (multiple recipients in one request), expect one webhook per recipient for submit-time events: data.job_id is typically shared across those callbacks, while data.message_id and data.recipient differ per message.
DLR events (sms.delivered, sms.failed, sms.expired) are keyed in this guide primarily by data.message_id, which is the stable id for message log and detail APIs. Use job_id to tie submit-time webhooks back to the send response; use message_id (and event) to follow a single message through delivery outcomes.
Product semantics
sms.sent— Briq (or the upstream provider) has accepted the message for routing after submit. This is not the same as confirmed delivery to the handset.sms.delivered,sms.failed,sms.expired— Reflect delivery-report (DLR) outcomes when Briq processes a final or terminal status from the carrier path.
When you might not get a DLR event
Briq only emits DLR webhooks when the reported status maps to delivered (sms.delivered) or failed / expired (sms.failed, sms.expired). Intermediate states (e.g. still pending at the carrier) do not produce those events until a qualifying update exists.
Submit-time events
Emitted when the send pipeline finishes its submit step for your message.event | When it fires |
|---|---|
sms.sent | Submit outcome is success (message accepted for sending / routing). |
sms.failed | Submit outcome is not success. |
data object (submit-time path, e.g. sms.sent):
| Field | Type | Description |
|---|---|---|
job_id | string | Same id as the job_id in the send API success response; use it to join webhooks to the request that returned it |
message_id | string | Briq message id for this recipient (once assigned) |
recipient | string | Destination number (digits with country code, e.g. 255758786077) |
status | string | Submit pipeline status label (e.g. SENT); treat as an opaque enum |
submitted_to_provider | string | When present: UTC ISO-8601 with milliseconds and Z |
data for sms.sent:
DLR (delivery report) events
Emitted when Briq applies a delivery report and maps it to a terminal-style outcome.event | When it fires |
|---|---|
sms.delivered | Carrier / pipeline reports successful delivery (mapped to a SENT outcome in Briq’s model). |
sms.expired | Message failed and the carrier response indicates expiry (e.g. not delivered within validity). |
sms.failed | Message failed without an expiry classification, or generic failure. |
data object (DLR path):
| Field | Type | Description |
|---|---|---|
message_id | string | Briq message id |
status | string | Status after the update (e.g. SENT, FAILED) |
delivered_at | string | On sms.delivered: UTC ISO-8601 ms + Z |
expired_at | string | On sms.expired: UTC ISO-8601 ms + Z |
failed_at | string | On sms.failed (DLR): UTC ISO-8601 ms + Z |
status strings match Briq’s internal lifecycle labels; treat them as opaque enums and branch on event first.
What your endpoint must return
| Your HTTP response | Briq behavior |
|---|---|
| Any 2xx | Treated as success; Briq stops retrying this attempt. |
| Non-2xx, timeout, or connection failure | Treated as failure; Briq retries with exponential backoff until a maximum attempt count is reached. |
Typical retry behavior
Unless your Briq environment is configured differently, defaults are often in this range:| Parameter | Typical value | Meaning |
|---|---|---|
| Max attempts | 5 | Delivery attempts per event |
| Request timeout | 10 seconds | How long Briq waits per attempt |
| Backoff | Exponential from ~30 s, capped around 3600 s, with jitter ~15% | Delay before retry after a failure |
Security checklist
- HTTPS only — Webhook URLs must use
https://. Briq rejects non-HTTPS URLs and common unsafe targets (e.g.localhost, addresses that resolve to private or loopback networks) at registration time. - Verify
X-Briq-Signaturewhen your webhook has a secret. - Check
app_id(andX-Briq-App-ID) match the app you expect. - Replay policy — The payload includes
created_at. Briq does not publish a standard replay window for integrators; if you need time-based rejection, define your own rule (e.g. ignore events older than N minutes) and document it internally.
Worked example (sms.sent)
Your path and host will differ; proxy headers (e.g. X-Forwarded-*) depend on your edge.
Optional: inspecting deliveries
Some Briq setups expose dashboard or API features to list webhook delivery attempts, view errors, or trigger retries. Availability, URLs, and authentication are deployment- and product-specific. Use whatever your Briq operator documents—do not rely on internal or test-only routes unless explicitly provided to you.Related guides
- Developer apps — API keys and app configuration in Briq.
- Messaging — Sending SMS and delivery concepts.
- WhatsApp delivery events:
whatsapp.sent/delivered/read/failedevents and verification. - Webhook Management API: Register webhooks, reveal secrets, and inspect or retry deliveries.