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

EventFires when
message.receivedA customer sent an inbound message.
message.sentA provider accepted your outbound message.
message.deliveredDelivery to the recipient was confirmed.
message.failedThe send failed (the payload includes the reason).
contact.created / contact.updatedContact records changed.
flow.submittedA 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 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) under Developers → Deliveries.
  • 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.