merce.STACK
merce.STACK
Skip to content
PricingWhat's new
Log inStart sending
{}Email API docs
GuidesPlatform docsGet API keys →
  • Email API

    • Email API
    • Sending email
    • Delivery events

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.

StatusMeans
scheduledHeld until scheduledAt.
sentAccepted for delivery from your domain.
deliveredThe recipient's mail server accepted it.
delayedStill being tried — a full mailbox, a slow server, a soft bounce.
bouncedThe address does not exist. It is now suppressed.
complainedThe recipient marked it as spam. It is now suppressed.
failedIt never left: refused by us or by the provider. Not charged.
cancelledA 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.

TypeScript
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 ?? ""));
}
JSON
{
  "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.tags carries 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.

← PreviousSending email

On this page

  • Webhooks
  • Suppressions

One workspace for everything your business runs on — your team, customers, store, marketing and payouts in one place.

Start sending

Product

  • All products
  • Pricing
  • What's new
  • Security

Resources

  • Documentation
  • Guides
  • Use cases
  • Blog
  • Get support

Company

  • About
  • Careers
  • Contact us
  • Talk to the team

Legal

  • Privacy
  • Terms
  • Cookie policy
  • GDPR
  • Report a vulnerability

© 2026 Mercestack. All rights reserved.

Mercestack StructureOS

mercestack