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.
To send events into Blueticked from another system, use Receive events from your app instead. The connection address and authorization are different.
Events
| Event | Fires when |
|---|---|
message.received | A customer sent an inbound message into a connected channel. |
message.sent | An outbound message was accepted by the provider. |
message.delivered | A provider confirmed delivery to the recipient. |
message.read | A provider confirmed the recipient read the message. |
message.failed | A provider or Blueticked failed to send the message. |
email.opened | A recipient opened an email (first open per message). |
email.clicked | A recipient clicked a link in an email. |
email.bounced | An email hard-bounced and the address was suppressed. |
email.complained | A recipient marked an email as spam; the address was suppressed. |
contact.created | A contact was created through the API or workspace UI. |
contact.updated | A contact profile or channel flag changed. |
suppression.created | An identifier was added to the do-not-contact list (STOP, unsubscribe, manual or API add). |
suppression.removed | A do-not-contact entry was removed (START resubscribe, manual or API delete). |
suppression.imported | A bulk opt-out import finished — one digest with counts, not one event per row. |
flow.submitted | A customer completed a WhatsApp Flow. |
channel.connected | A channel was connected to a business — including when your customer completed a connect link you sent them. |
agent.run.completed | An AI agent completed a handled run. |
ticket.created | A ticket was raised — by a flow's create-a-ticket step, by hand from a conversation, or via the API. |
ticket.assigned | A ticket was put on someone's desk (assignee set or changed). |
ticket.updated | A ticket's status, priority or title changed (excluding resolve/reopen, which have their own events). |
ticket.reopened | A resolved ticket was reopened. |
ticket.resolved | A ticket was marked resolved in Control Centre (or via the API). |
commerce.order.paid | A shop order was paid (confirmed by the signed payment webhook). |
commerce.order.fulfilled | A paid order was marked handed over — shipped, collected, or courier-booked. |
commerce.order.refunded | A paid order was refunded in full. |
commerce.order.abandoned | A checkout was started but never paid — the recovery hook for a follow-up automation. |
table_row.created | A row was added to a table — by a form, the Inbox, an import, the API or by hand. |
table_row.status_changed | A table row moved to another status. |
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) on Developers → Webhooks, in its delivery log. Incoming connection events have their own Deliveries tab under Connected apps → Integrations.
- 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.