Provider guide

Resend webhooks, kept properly.

Resend uses Standard Webhooks, which is the closest thing this space has to a convention: an id, a timestamp and a signature over all three parts. Pleasant to work with, and still worth keeping every delivery of.

Field notes

How Resend behaves, and where it bites

A published standard rather than a bespoke scheme, which is rarer than it should be.

How Resend sends

Resend POSTs JSON with the type in type (email.delivered, email.bounced) and the Standard Webhooks trio of svix-id, svix-timestamp and svix-signature.

Where it bites

  • Delivery events are your only bounce record. If a webhook is missed, the reason an email failed is not something you can reconstruct later.
  • The signature covers id, timestamp and body. Verifying only the body passes things it should not, which is a subtle way to get it wrong.
  • Secrets are prefixed and easy to paste wrongly. A whsec_ value has to be handled as the standard describes, not as a plain string.

The vault under it

Point a WebhookVault endpoint at Resend and every delivery is stored in full: headers verbatim, bodies byte-for-byte, labelled with its event type and searchable. Forward it onward to your own handler if you want the relay pattern, replay any delivery once the bug is fixed, and let a Watchdog notice the day it stops arriving.

01

Create an endpoint

Name it resend-events. Turn on forwarding to your own handler if you want the vault in front of it rather than beside it.

02

Register the capture URL

Resend → Webhooks → Add Webhook. Paste the capture URL and select the events you care about.

03

Keep everything

Every delivery from here on is on the shelf: searchable, replayable, and watched for the day it goes quiet.

Verify from the record, not just at the door

Standard Webhooks signs id.timestamp.body and allows several signatures in one header so a secret can be rotated without dropping deliveries. The vault handles all of it.

The vault stores the signature headers verbatim with every request, so a delivery from last month can still be checked today. Verdicts are part of Solo and up. How verification works →

captured · email.bounced✓ verified
# headers, stored verbatim
"svix-id": "msg_2gT4kP…"
"svix-timestamp": "1757030400"
"svix-signature": "v1,8f3a2c41d9e6…"

{
  "type": "email.bounced",
  "created_at": "2026-09-11T06:14:22.190Z",
  "data": {
    "email_id": "4ef9a417-02e9-…",
    "to": [ "[email protected]" ],
    "subject": "Your September invoice"
  }
}

Event catalogue

Resend webhook events you'll see

A few of the events Resend sends, not the full list: Resend's own documentation has every one. Each arrives in the vault labelled with its event type.

EventWhat it tells you
email.sentResend accepted the email and sent it on.
email.deliveredThe receiving server accepted the email.
email.delivery_delayedDelivery was delayed; Resend is still trying.
email.bouncedThe receiving server refused the email permanently.
email.complainedThe recipient marked the email as spam.
email.failedResend could not send the email.

Questions

Resend webhook questions, answered

The three that come up most when Resend is wired to a new endpoint.

How does Resend retry failed webhooks?

If your endpoint does not answer with a 2xx, Resend retries on a growing backoff schedule over roughly a day, then stops. A WebhookVault capture URL answers as soon as the delivery is stored, so the event is kept even while your own handler is failing, and you can replay it once the fix is live.

How do I verify the Resend signature?

Resend follows Standard Webhooks. Sign {svix-id}.{svix-timestamp}.{raw body} with HMAC-SHA256, keyed with the base64-decoded part of your whsec_ secret, and compare the base64 result with each v1 value in svix-signature. Refuse a stale timestamp. On Solo and up the vault does this at capture.

Why did I get the same Resend event twice?

Delivery is at least once: a retry after a slow or failed response repeats the event. The svix-id header is the same on every attempt, so use it to de-duplicate. The vault stores the headers verbatim, so you can see which captures are repeats.

Keep the reason it bounced.

Free tier, no card. A capture URL in a few minutes.