Webhooks
SubsiMail can POST a signed JSON payload to any URL you configure, every time a campaign event happens — a send, an open, a click, a reply, an unsubscribe. That's the whole integration surface: point it at Zapier, Make, n8n, Slack (via a relay), your own CRM, or any service that can accept an HTTP POST, and everything downstream is entirely up to you.
Setting up a webhook
From your SubsiMail instance, go to Settings → Webhooks → New webhook. You'll need:
- The endpoint URL you want events POSTed to (POST only — no GET, so the full event payload can travel in the request body rather than a URL query string)
- Which events you want to receive (pick as many as you like — see below)
On save, SubsiMail generates a signing secret and shows it to you once. Copy it immediately — it's stored encrypted after that and can't be displayed again (you can always delete the webhook and create a new one if it's lost). You can create as many webhooks as you want, each with its own URL and its own event selection — e.g. one to Slack for replies, a separate one to your CRM for everything.
Available events
| Event | Fires when |
|---|---|
sent | A step was successfully sent to a contact |
opened | The tracking pixel loaded (approximate — see open tracking) |
clicked | A tracked link in the email was clicked |
replied | A contact replied (their sequence also stops) |
bounced / failed | Delivery failed at send time |
enrolled | A contact was newly enrolled into a campaign |
unsubscribed | A contact used the unsubscribe link |
Payload format
Every delivery is a JSON body like this:
{
"event": "replied",
"timestamp": "2026-07-20T10:15:32Z",
"campaign": { "id": 42, "name": "Q3 outbound" },
"contact": { "id": 918, "email": "jane@example.com", "name": "Jane Doe" },
"step_order": 2,
"url": null
}
step_order is set for send-related events and null otherwise; url is only set on clicked, carrying the destination link.
Verifying the signature
Every request includes an X-SubsiMail-Signature header: sha256= followed by the HMAC-SHA256 of the raw request body, hex-encoded, using your webhook's secret as the key — the same scheme GitHub webhooks use. Recompute it on your end and compare before trusting a payload:
const crypto = require('crypto');
function isValid(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}
Sign against the raw request body, not a re-serialized version of the parsed JSON — re-encoding can reorder keys or change whitespace and produce a signature mismatch even for a genuine request.
Auth header for gated endpoints
If the endpoint you're sending to needs its own credential to accept requests at all (a bearer token, an API key), set an auth header when creating the webhook — e.g. header name Authorization, value Bearer <your token>. It's sent on every request, stored encrypted, and is separate from signature verification above: the header gets you past your endpoint's own gate, the signature is how the endpoint confirms the request really came from SubsiMail.
Retries & delivery log
A delivery only counts as successful on an HTTP 200 or 201 response. Anything else — a different status code, a timeout, an unreachable host — is retried automatically with backoff (1 minute, then 5, 30, 2 hours, 6 hours) before being marked permanently failed after 6 attempts. The next real event still fires a fresh delivery regardless, so a temporarily-down endpoint recovers on its own.
Every webhook has its own delivery log (status, response code, attempt count) under Settings → Webhooks → Log, useful for debugging a misconfigured endpoint. There's also a Send test event button that fires a synthetic ping event through the exact same delivery pipeline, so you can confirm connectivity before wiring up anything real. Delivery records are kept for 7 days, then deleted automatically.
Connecting to Zapier, Make, n8n, and others
Because this is a plain signed webhook, it works with anything that can receive one — no SubsiMail-specific app or plugin required:
- Zapier — use a "Webhooks by Zapier" → Catch Hook trigger, paste the generated URL in as your SubsiMail webhook endpoint, and build any Zap from there (Slack messages, Google Sheets rows, CRM updates, anything Zapier connects to).
- Make (Integromat) — add a Webhooks → Custom webhook module as your scenario's trigger, same idea.
- n8n — a Webhook node as your workflow's trigger; n8n's Crypto node can verify the HMAC signature before continuing.
- Slack — Slack's own Incoming Webhooks don't accept arbitrary payloads, so route through Zapier/Make/n8n (any of the above) to reformat the event into a Slack message.
- Your own service — a few lines of server code: verify the signature, parse the JSON, do whatever you need.