At Mailpro the word “webhook” covers three different mechanisms. They serve different needs and are configured in different places — the direction of the arrow makes all the difference.
Be notified in real time when something happens in your account: email delivered, bounce, new contact, SMS delivered, low credits… Signed JSON POST to your URL, with automatic retries.
POST /Webhook (CRM API v3), POST /webhooks (Email / SMS API v2).At a given step of a workflow, push information to Slack, your ERP, a Google Sheet, Zapier… JSON body with variables, custom headers, optional HMAC signing key (X-Mailpro-Automation-Signature). No extra credit.
Start a Mailpro automation from outside: a Stripe payment, a Typeform answer, a sign-up on your website. A secret URL, shown once when the workflow is activated; any JSON body up to 256 KB; answers 202 Accepted.
Up to 50 events per webhook, 20 webhooks per account. The technical name is what arrives in the type field of the envelope. The data payload of each type is documented in the API reference.
Every event shares the same JSON envelope. Use id as an idempotency key: a retried delivery carries the same id.
{
"id": "evt_5f2c…",
"type": "email.delivered",
"created": 1758000000,
"apiVersion": "v3",
"account": { "id": 12345, "name": "ACME" },
"data": {
"email": "[email protected]",
"idSending": 987654,
"sendingType": 3,
"diagCode": "250 2.0.0 OK",
"deliveredAt": "2026-09-16T08:03:41Z"
}
}
Each call carries X-Mailpro-Signature: t=<unix>,v1=<hex>. v1 is the HMAC-SHA256 of t + "." + raw body with the secret shown once at creation. Reject stale timestamps, answer 2xx within 10 seconds, process asynchronously.
On failure Mailpro retries 10 s → 1 min → 5 min → 30 min → 2 h → 6 h → 24 h, then disables the webhook after 10 consecutive failures (one click to reactivate).
// Node.js
const crypto = require("crypto");
function verify(rawBody, header, secret) {
const t = header.match(/t=([0-9]+)/)[1];
const v1 = header.match(/v1=([0-9a-f]+)/)[1];
const expected = crypto.createHmac("sha256", secret)
.update(t + "." + rawBody).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
Every single send (API v2, API v3, SMTP relay, automation — not campaigns) returns an identifier. Poll GET /Send/{id}/Status (v3) or GET /send/{id}/status.json (v2), or subscribe to email.delivered / email.bounced. The delivered status appears about 5 minutes after the handover and is kept for 365 days.
| status | Meaning |
|---|---|
queued | Accepted, waiting to be sent. |
scheduled | Planned for a later date. |
sent | Handed over by our servers, no answer from the recipient server yet. |
deferred | The recipient server asked us to retry later — we do. |
delivered | Accepted by the recipient server (diag_code = its SMTP reply). |
bounced | Rejected — bounce_type tells permanent from temporary. |
not_found | Unknown id, or an id that does not belong to your account. |
/Send/{id}/Status./webhooks, /send/{id}/status.