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:
- Update internal state the moment a message is delivered or bounces.
- React to compliance events (opt-outs, complaints) without manual review.
- Trigger downstream automation (CRM updates, customer success workflows).
Creating an endpoint
- Open Developer → Webhooks.
- Click Add endpoint.
- Enter the URL that will receive events. Must be HTTPS with a valid TLS certificate.
- Pick the event types you care about (at least one).
- 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 |
| 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:
Blocx-Signature— formatted ast=<unix-timestamp>,v1=<hex>wherev1 = HMAC-SHA256(secret, "${t}.${rawBody}").Blocx-EventandBlocx-Event-Id— the event type and a unique event ID for idempotent processing.
To verify:
- Read the raw request body before parsing JSON — the signature is over raw bytes.
- Parse
tandv1fromBlocx-Signature. - Compute
HMAC-SHA256(secret, "${t}.${rawBody}")and compare tov1in constant time. - Reject requests whose
tis 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
- Respond fast. Return a 2xx within a couple of seconds. Queue heavy work; don't block on it.
- Don't fetch the API to enrich. The payload contains the relevant fields; calling back amplifies traffic.
- Scale horizontally. Bursts of
message.deliveredoremail.deliveredcan be substantial at peak.