Webhooks and signing
Webhooks push events to your server as they happen — use them instead of polling. Create subscriptions in the dashboard under Developers → Webhooks, choosing which events each endpoint receives.
Events
| Event | Fires when |
|---|---|
message.received | A customer sent an inbound message. |
message.sent | A provider accepted your outbound message. |
message.delivered | Delivery to the recipient was confirmed. |
message.failed | The send failed (the payload includes the reason). |
contact.created / contact.updated | Contact records changed. |
flow.submitted | A customer completed a WhatsApp Flow (form). |
Verify every delivery
Each request carries x-blueticked-event (the event name) and x-blueticked-signature — sha256=<hex HMAC-SHA256 of the raw body> keyed with your endpoint secret. Always verify against the raw body; re-serializing parsed JSON breaks the signature.
verify.ts (SDK)
const event = await bt.webhooks.constructEvent(
rawBody,
req.headers.get("x-blueticked-signature") ?? "",
process.env.BLUETICKED_WEBHOOK_SECRET!,
); // throws on a bad signature — return 401 in your catchverify.ts (no SDK, Node)
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, signature: string, secret: string): boolean {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return (
expected.length === signature.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
);
}Delivery and retries
- Respond with any 2xx within 10 seconds; do slow work asynchronously.
- Failed deliveries are retried with backoff; every attempt is visible (and replayable) under Developers → Deliveries.
- Sandbox (
blu_test_) traffic fires the same events with"test": truein the payload — branch on it to keep test data out of production side effects. - Deliveries can arrive out of order or more than once; treat handlers as idempotent.