Guides

Webhooks (Email → API)

The payload you receive and how to verify its signature.

When mail arrives at an endpoint with a webhook destination, BetterMX POSTs a JSON payload to your URL.

json
{
  "id": "msg_2b4f…",
  "endpoint_id": "ep_9f…",
  "received_at": "2026-06-16T10:24:01Z",
  "from": { "address": "john@acme.com", "name": "John Doe" },
  "subject": "PO #4821",
  "text": "Need 12 units.",
  "html": "<p>Need 12 units.</p>",
  "attachments": [{ "filename": "po.pdf", "size": 18234, "sha256": "…" }],
  "authentication": { "spf": "pass", "dkim": "pass", "dmarc": "pass" }
}

Verifying the signature

Each delivery carries BetterMX-Signature: t=<unix>,v1=<hex>. Recompute the HMAC-SHA256 of "{t}.{rawBody}" with your signing secret, compare in constant time, and reject timestamps older than five minutes.

javascript
import crypto from "node:crypto";
function verify(body, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map(p => p.split("=")));
  const mac = crypto.createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
  if (mac !== v1) throw new Error("bad signature");
  if (Math.abs(Date.now()/1000 - Number(t)) > 300) throw new Error("stale");
}

Retries & replay

Non-2xx responses are retried with exponential backoff. Delivery is at-least-once and idempotent on the message id — dedupe on it. Failed deliveries can be replayed from the dashboard.