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

EventFires when
message.receivedA customer sent an inbound message into a connected channel.
message.sentAn outbound message was accepted by the provider.
message.deliveredA provider confirmed delivery to the recipient.
message.readA provider confirmed the recipient read the message.
message.failedA provider or Blueticked failed to send the message.
email.openedA recipient opened an email (first open per message).
email.clickedA recipient clicked a link in an email.
email.bouncedAn email hard-bounced and the address was suppressed.
email.complainedA recipient marked an email as spam; the address was suppressed.
contact.createdA contact was created through the API or workspace UI.
contact.updatedA contact profile or channel flag changed.
suppression.createdAn identifier was added to the do-not-contact list (STOP, unsubscribe, manual or API add).
suppression.removedA do-not-contact entry was removed (START resubscribe, manual or API delete).
suppression.importedA bulk opt-out import finished — one digest with counts, not one event per row.
flow.submittedA customer completed a WhatsApp Flow.
channel.connectedA channel was connected to a business — including when your customer completed a connect link you sent them.
agent.run.completedAn AI agent completed a handled run.
ticket.createdA ticket was raised — by a flow's create-a-ticket step, by hand from a conversation, or via the API.
ticket.assignedA ticket was put on someone's desk (assignee set or changed).
ticket.updatedA ticket's status, priority or title changed (excluding resolve/reopen, which have their own events).
ticket.reopenedA resolved ticket was reopened.
ticket.resolvedA ticket was marked resolved in Control Centre (or via the API).
commerce.order.paidA shop order was paid (confirmed by the signed payment webhook).
commerce.order.fulfilledA paid order was marked handed over — shipped, collected, or courier-booked.
commerce.order.refundedA paid order was refunded in full.
commerce.order.abandonedA checkout was started but never paid — the recovery hook for a follow-up automation.
table_row.createdA row was added to a table — by a form, the Inbox, an import, the API or by hand.
table_row.status_changedA 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 catch
verify.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": true in 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.