Webhooks guide
Receive message events at your own URL with HMAC-SHA256 verification.
As a message moves through its lifecycle - and when a customer replies on WhatsApp - Teekrr POSTs a JSON event to every active webhook endpoint you’ve registered for that event type. Webhooks are managed in-app at /api-management → Webhooks.
The five event types
The email channel emits no webhook events. Email broadcasts are dispatched synchronously - the sent / failed / total counts in the POST /email response are the complete result. Use the dashboard’s Message Logs for per-recipient detail.
You can register an Email-channel subscription through the API, but it will never fire.
Headers Teekrr sends
Payload envelope
Every body shares this five-key envelope:
channeltells you how to parsedata- the SMS and WhatsApp payloads have different field names. Always read it before touchingdata.api_keynames the integration whose broadcast produced the event, so a consumer whose several keys point at one endpoint can tell them apart. It isnullfor a dashboard-composed send and forinbound.
All data field names are snake_case. See the Webhooks group in the API Reference for the full per-event schemas; in short:
Which events you receive
Four filters decide whether a given event reaches a given endpoint. All four must pass.
event_types- required on every subscription, minimum one. You only receive the events you list.channels- optional. Omit it (or leave itnull) to receive every channel; supply a subset to narrow. An empty array is rejected at registration.api_key_uuid- optional. Bind a subscription to one API key so it only fires for that integration’s broadcasts.nullmeans any key.- Broadcast origin - status events (
sent,delivered,read,failed) fan out only for broadcasts sent through the API. A broadcast composed in the dashboard has no integration waiting on a callback and emits nothing.
Point 4 is the usual answer to “I registered a webhook and sent a test broadcast from the dashboard, but nothing arrived.” Send via POST /sms or POST /whatsapp to see status events.
inbound is exempt - a customer’s reply has no origin to attribute, so you receive it regardless of how the original message was sent. Inbound messages on the shared platform-default WhatsApp number are not attributable to one client and never fire.
Verifying the signature
The X-Teekrr-Signature header is not a bare hex digest. It carries a timestamp and a versioned signature:
To verify:
- Parse
t(unix seconds) andv1(hex) out of the header. - Build the signed string as
`${t}.${rawBody}`- the timestamp, a literal., then the raw, untouched request body. - Compute
HMAC-SHA256of that string using your webhook’s plaintext signing secret (thewhsec_…value shown once at creation) as the key. - Compare against
v1with a constant-time comparison. - Reject the request if
tis more than 5 minutes old.
The HMAC key is the plaintext secret exactly as issued - do not hash it first.
The timestamp is signed alongside the body, not merely sent with it. A signature over the body alone would stay valid forever, so a captured delivery could be replayed at any time and still verify. Enforce the age window in your handler - step 5 above.
Always use the raw, untouched request body. JSON parsing changes whitespace and breaks the signature. Express needs express.raw({ type: "application/json" }). FastAPI needs await request.body() before request.json().
The signing secret
The plaintext secret (whsec_ followed by 40 hex characters) is returned once, in the 201 response when you create the webhook. Store it server-side immediately - afterwards only its first 12 characters are retrievable, and there is no way to recover the rest. If you lose it, delete the webhook and create a new one.
Retries & timeouts
- Teekrr applies a 10-second timeout per delivery.
- Success is any
2xx. Everything else is recorded as a failure. - Redirects are not followed. A
3xxresponse is a hard failure - register the final URL directly. - Teekrr does not retry failed deliveries. Design your endpoint to be idempotent anyway, and use the dashboard’s Message Logs as a backstop.
- Your endpoint must be a public HTTPS address. Endpoints that stop resolving to one are blocked, and the attempt is recorded in your delivery logs.
- Every delivery attempt is recorded at
/api-management → Webhooks → Delivery Logs, including the first 512 characters of your response body.
Idempotency
There is no delivery-level event ID. De-duplicate on the provider message identifier inside data, paired with the event name:
- SMS -
(event, data.message_id) - WhatsApp -
(event, data.provider_message_id)
A single message legitimately produces several events (sent, then delivered, then read), so the event name has to be part of the key.