Email Fast Get a sandbox key
Features

Events you can verify before you trust

Seven event types, one envelope, timestamped signatures, and delivery hardened against SSRF. If the signature doesn't verify, it isn't ours.

Every message lifecycle, pushed to you

Email webhooks deliver the full lifecycle to your endpoint: email.sent, email.bounced, email.opened, email.clicked, email.complained, email.unsubscribed, and email.inbound. Events arrive as one envelope shape — { "id": "evt_…", "type", "created_at", "data" } — and as webhooks signed with timestamped HMAC-SHA256 (Stripe-style t=…,v1=…), with delivery hardened against server-side request forgery.

How it works

  1. Subscribe an HTTPS endpoint to the event types you want.
  2. On each delivery, read three headers: x-emailfast-signature (t=<unix>,v1=<hmac>), x-emailfast-timestamp, and x-emailfast-event.
  3. Recompute the HMAC-SHA256 over the timestamp and raw body with your endpoint secret; reject mismatches and anything older than the five-minute replay tolerance.
  4. Return a 2xx. Anything else is retried — up to 15 attempts with backoff.

Delivery is SSRF-guarded: destination URLs are resolved and checked before we connect, so the webhook system can't be aimed at private networks and used as a probe.

The evidence

One delivery, verifiable offline
x-emailfast-event: email.bounced
x-emailfast-timestamp: 1752750000
x-emailfast-signature: t=1752750000,v1=8f1c2a...
{
  "id": "evt_01h9...",
  "type": "email.bounced",
  "created_at": "2026-07-17T09:00:00Z",
  "data": { "message_id": "msg_01h9..." }
}

Verification needs nothing from us at request time: your secret, the timestamp, the raw body. A tampered byte or a five-minute-old replay fails verification.

Honest limits

At-least-once, not exactly-once

Delivery is at-least-once: rare duplicates happen, so make handlers idempotent on the evt_ id. Ordering across event types isn't guaranteed — don't build state machines on arrival order. And 15 retries is a lot, but not forever: an endpoint that stays down will miss events, which is what message timelines are for. Reconcile by pulling, not only by listening.

Where to go next

Wire a verified handler in minutes with the Node quickstart. Webhooks pair with the send API on the way out and inbound parsing on the way in. Weighing providers on the event surface? The Resend comparison covers it honestly.

Questions, answered plainly

How do I verify a webhook?

Recompute the HMAC-SHA256 over the timestamp and raw request body with your endpoint secret, compare it to the v1= value in x-emailfast-signature, and reject anything whose timestamp is outside the five-minute tolerance. No call to us is needed — verification is offline.

What stops replay attacks?

The timestamp is inside the signed material. An attacker replaying a captured delivery can't refresh the timestamp without breaking the signature, and anything older than five minutes is rejected.

What's the retry policy?

Any non-2xx response is retried — up to 15 attempts with backoff. After that, delivery stops for that event; reconcile by pulling the message timeline with GET /v1/messages/:id.

Will I ever see the same event twice?

Rarely, yes — delivery is at-least-once. Make handlers idempotent on the evt_ id and duplicates become harmless.

See it for yourself

Sandbox keys run the real pipeline dry — real validation, real events, a hosted inbox, no email sent. Early access is onboarding now.