Configuring Webhooks

Webhooks deliver messaging and email events to your application as soon as they happen — message delivered, email bounced, inbound message received. Manage endpoints at Developer → Webhooks.

Why webhooks

Polling the API for status doesn't scale. Webhooks let you:

Creating an endpoint

  1. Open Developer → Webhooks.
  2. Click Add endpoint.
  3. Enter the URL that will receive events. Must be HTTPS with a valid TLS certificate.
  4. Pick the event types you care about (at least one).
  5. Save. Blocx shows the signing secret — copy it to your secret manager.

Event catalog

The available event types are:

Messages

Event When it fires
message.sent Message accepted by carrier
message.delivered Message delivered to recipient
message.failed Message failed to deliver
message.received Inbound message received

Email

Event When it fires
email.delivered Email delivered to recipient
email.bounced Email bounced
email.complained Recipient marked the message as spam
email.opened Recipient opened the email
email.clicked Recipient clicked a link
email.received Inbound email received

For billing or compliance changes (auto-recharge results, balance alerts, brand or campaign status, port progress, domain verification), organization owners and admins receive in-app and email notifications automatically — webhook subscriptions for those events are not currently offered.

Payload shape

{
  "id": "evt_01HRZ4...",
  "type": "message.delivered",
  "created_at": "2026-05-11T17:14:02Z",
  "org_id": "org_01...",
  "data": {
    "id": "msg_01...",
    "from": "+15551234567",
    "to":   "+15557654321",
    "status": "delivered",
    "delivered_at": "2026-05-11T17:14:01Z"
  }
}

Verifying signatures

Every request includes a Stripe-style signature header:

To verify:

  1. Read the raw request body before parsing JSON — the signature is over raw bytes.
  2. Parse t and v1 from Blocx-Signature.
  3. Compute HMAC-SHA256(secret, "${t}.${rawBody}") and compare to v1 in constant time.
  4. Reject requests whose t is more than 5 minutes off from your server's clock to prevent replay.
import crypto from 'crypto'

function verify(rawBody: string, signatureHeader: string, secret: string) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((p) => p.split('='))
  ) as { t?: string; v1?: string }
  if (!parts.t || !parts.v1) return false
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}

Idempotency

Use the Blocx-Event-Id header to deduplicate. Retries or accidental redeliveries reuse the same event ID, so storing seen IDs (briefly) lets your handler skip duplicates safely.

Performance tips

Related articles