Skip to content
WeZend

Core APIs

Webhooks

How per-message delivery webhooks actually work: the webhook_url field, real headers, and the real retry schedule.

WeZend's delivery webhooks are per-message: you pass webhook_url directly on the send request (or per-message inside a bulk request), and status updates for that message are POSTed there as they happen. For account-wide platform events there is also /v1/webhook-endpoints — see below.

{
  "to": "+4512345678",
  "channel": "sms",
  "message": "Your order has shipped.",
  "webhook_url": "https://yourapp.com/hooks/wezend"
}

Payload and headers

Every delivery includes:

  • X-WeZend-Event — the event name
  • X-WeZend-Timestamp — Unix ms timestamp used in the signature
  • X-WeZend-Signature — HMAC-SHA256 of ${timestamp}.${JSON.stringify(payload)}, using the webhook signing secret from Settings → Webhook secret

Every delivery also carries legacy X-ZafeConnect-* aliases of the same three headers, kept from before the rebrand so existing integrations keep verifying. The values are identical to the X-WeZend-* headers — verify against whichever prefix you already use.

Verify the signature

const crypto = require("crypto");

function verify(rawBody, timestamp, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

Retries

If your endpoint doesn't respond 2xx, delivery is retried 3 times — after 30 seconds, 5 minutes, then 30 minutes — and retries survive a restart or deploy (they're durable, backed by the database, not an in-memory queue). Respond quickly and process asynchronously rather than doing slow work in the request handler.

Platform-event webhooks

Besides per-message delivery webhooks, you can register account-wide outbound endpoints: POST /v1/webhook-endpoints with { url, events } creates one (manage with GET/PATCH/DELETE, and POST /v1/webhook-endpoints/:id/test delivers a ping). Either credential works — an API key or a dashboard JWT from POST /v1/auth/login — so a server-side integration can subscribe itself without anyone opening the dashboard. Each endpoint gets its own whsec_... signing secret, shown once at creation.

curl -X POST https://api.wezend.com/v1/webhook-endpoints \
  -H "X-API-Key: $WEZEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/hooks/wezend", "events": ["contact.unsubscribed"] }'

If the key was issued with an explicit scope list, it needs write to create, update or delete an endpoint. A read-scoped key can list endpoints but gets 403 insufficient_scope on the mutations, with the missing scope named in the response body. Keys issued without a scope list keep full access.

Nine event types can be subscribed to in events. An endpoint registered with an empty events array receives all of them.

EventFires when
contact.createdA contact is created
contact.updatedA contact's fields or traits change
contact.unsubscribedNew — a contact opts out. Emitted from both suppression writers: the unsubscribe path and the SMS STOP-keyword path
contact.bouncedNew — email feedback reports a bounce for the contact
message.deliveredA message is confirmed delivered
message.failedA message fails permanently
message.receivedNew — an inbound email reply arrives
campaign.sentA campaign finishes sending
form.submittedA hosted form is submitted. The event name was already in the list, but nothing emitted it before this release — it now fires on every submission

Subscribe to contact.unsubscribed to keep your own consent records in sync. Before this event existed, nothing pushed an opt-out back out of WeZend: if your system of record is something else — a CRM, a dealer portal, your own database — there was no way to learn that a contact had replied STOP, and you would keep treating them as subscribed. contact.bounced is a deliverability signal rather than an explicit opt-out, so keep the two apart in your own data.

Inbound vendor delivery receipts

The /v1/webhooks/messente, /v1/webhooks/twilio, /v1/webhooks/vonage and /v1/webhooks/whatsapp endpoints are how upstream carriers report delivery back to the platform — they're platform-internal and not something you configure; they exist so WeZend itself can populate the statuses you see via webhook_url and the message history API.

One platform. Every customer interaction.

Replace your patchwork of messaging APIs, CDP and automation tools with a single engagement platform built for scale.

No credit card required · EU data residency · 99.99% uptime SLA