All packsTransactional Email Deliverability CoreSolve
resend webhook dedupe svix id vs email id
email_id looks like the dedupe key. It isn't.
Resend's data.email_id is shared across a message's bounced, delivered, and complained events -- deduping on it collapses distinct events into one.
What goes wrong
Resend webhook payloads carry no top-level event id, and the obvious candidate -- data.email_id -- is shared across every event type that happens to one message: sent, delivered, bounced, and complained can all carry the same email_id. Deduplicating on it looks correct in a quick test (the same event delivered twice does dedupe) and silently collapses genuinely distinct events into one, dropping real signal like a complaint that arrived after a delivery notification for the same message.
Why agents get it wrong
email_id is right there in the payload, named like an identifier, and does correctly dedupe the retry-of-the-same-event case, which is the case most webhook-handling code is written and tested against. It takes reading Resend's event-shape documentation specifically for "can two different event types share this field" to notice the collision -- not something a dedupe implementation naturally surfaces on its own.
What Transactional Email Deliverability Core pre-decides
The pack's webhook decisions dedupe on the svix-id header instead -- unique per delivery attempt group, supplied by the Svix transport layer Resend signs with, not by Resend's own payload. It's stored as email_events.provider_event_id with a UNIQUE constraint, so the insert itself is the idempotency check: a duplicate delivery throws a conflict, is reported as {duplicate: true}, and the route still returns 200 so Svix doesn't retry forever.
Source: ARCHITECTURE.md 'Webhooks (Resend -> Svix transport)', decision 3