Agent-readable docs index: /docs/llms.txt. Full docs in one file: /docs/llms-full.txt. Download /docs/docs.zip to grep all markdown files locally.

Receive and verify signed delivery webhooks

Webhooks send event notifications from Convert to your server. Create an endpoint under Webhooks and select the events your integration needs. An empty event selection means all events.

Supported events

  • Message delivery: message.sent, message.delivered, message.read, message.failed.
  • Inbound WhatsApp: message.received.
  • Email outcomes/engagement: message.bounced, message.unsubscribed, message.opened, message.clicked.
  • Audience/workflow activity: campaign.completed, contact.created, form.submitted.
Events depend on channel support. Email opens can be influenced by privacy features. Campaign completed describes send-phase completion, not universal receipt or engagement.

Receive the payload

Convert POSTs JSON with an event name, timestamp, and event-specific data. Treat timestamps and provider IDs as useful context, and inspect the fields for the actual event you subscribe to rather than assuming all event types share one schema.
{ "event": "message.delivered", "created_at": "2026-10-11T09:00:00Z", "data": { "message_id": 8841, "to": "+2348012345678", "channel": "whatsapp", "status": "delivered", "provider_message_id": "wamid.example" } }

Verify the signature

Use the endpoint's signing secret to compute HMAC-SHA256 over the exact raw request bytes. The header format is X-Convert-Signature: sha256=<hex>. Verify before processing; JSON parsing and reserialization changes the signed bytes.
import crypto from "node:crypto"; export function verifyConvertSignature(rawBody, signature, secret) { if (!Buffer.isBuffer(rawBody) || typeof signature !== "string") return false; const expected = Buffer.from( "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex"), "utf8" ); const supplied = Buffer.from(signature, "utf8"); return supplied.length === expected.length && crypto.timingSafeEqual(supplied, expected); }
Configure raw-body middleware for your framework before its JSON parser. The example guards unequal lengths so invalid headers cannot make timingSafeEqual throw. Store the secret server-side and keep it separate from your Bearer API key.

Acknowledge and retry

Return a 2xx response after safely accepting the event. Queue expensive application processing so the receiver responds promptly. Connection failures, timeouts, and non-2xx responses are retried with backoff up to five attempts.
Design for duplicates. Persist a suitable event identity/correlation key and make business updates idempotent. Delivery events can arrive later than your initiating API response; compare existing state rather than blindly decrementing counters on every notification.

Endpoint requirements

Use a reachable public HTTP(S) endpoint, preferably HTTPS. Local/private destinations are rejected by outbound URL checks. Ensure your ingress accepts the request, retains the signature header, and does not rewrite the body.
Save verified events before acknowledging. This avoids losing a delivery update if your downstream processing fails after a successful response.