Email API
Delivery events
What happened after “sent”: statuses, webhooks and the suppression list.
Every message is reported on after it leaves. Each report moves the message's status forward — never back — is kept on the message, and is sent to your webhook endpoints.
| Status | Means |
|---|---|
scheduled | Held until scheduledAt. |
sent | Accepted for delivery from your domain. |
delivered | The recipient's mail server accepted it. |
delayed | Still being tried — a full mailbox, a slow server, a soft bounce. |
bounced | The address does not exist. It is now suppressed. |
complained | The recipient marked it as spam. It is now suppressed. |
failed | It never left: refused by us or by the provider. Not charged. |
cancelled | A scheduled message you called off. |
Webhooks
Add endpoints in Email API → Webhooks in your workspace; each endpoint's signing secret is shown once, when you add it. Deliveries are signed, retried after 30 seconds, 2, 10 and 30 minutes, and every attempt is logged there. Deduplicate on the event id — delivery is at least once.
The Mercestack-Signature header is t=<unix>,v1=<hex>: an HMAC-SHA256, with the endpoint secret, of "<t>.<raw body>". Check it against the raw body, and reject a timestamp more than a few minutes old.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, header: string, secret: string) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("=") as [string, string]),
);
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}{
"id": "e91b…",
"type": "email.bounced",
"createdAt": "2026-10-25T09:00:02.000Z",
"livemode": true,
"data": {
"id": "6f1c2a90-…",
"to": ["[email protected]"],
"from": "[email protected]",
"subject": "Your receipt",
"tags": { "category": "receipt" },
"recipients": ["[email protected]"],
"bounceType": "Permanent",
"diagnostic": "smtp; 550 5.1.1 user unknown"
}
}email.sent,email.delivered,email.delayed,email.bounced,email.complained,email.failed.data.tagscarries the tags you sent, so you can route an event without looking the message up.
Suppressions
A hard bounce or a spam complaint puts the address on your workspace's suppression list automatically. A send to a suppressed address is refused with 422 before anything leaves — a bounce is a fact about the address, and mailing it again hurts your domain. The list is shared with Mercestack Marketing, and you can add or remove addresses in Email API → Suppressions. An unsubscribe can only be undone by the person who made it.