π¬ Email Webhook Receiver
Receives delivery events from Resend, SES, Mailgun and SMTP2GO, verifies them, and writes the recipient ledger. It does not send mail.
This service does not send email
There is no HTTP send API here, and there is no API token to obtain. Sending happens in-process through the @eventjuicer/email drivers, orchestrated by Trigger.dev tasks in packages/trigger/src/tasks/email/.
Provider credentials resolve per organizer and per environment from the AccessKey table β never from this deployment's environment.
π Routes
POST /api/organizers/{organizer_id}/webhooks/resend
Preferred. Tenant comes from the URL, so untagged events are accepted too.
POST /api/organizers/{organizer_id}/webhooks/ses
Preferred. SNS notifications, verified against the organizerβs pinned topic.
POST /api/webhooks/{driver}
Legacy. Resolves the tenant from the message tag, so it only accepts tagged events.
GET /api/health
Liveness and the one environment variable this service genuinely needs.
π How a request is trusted
There is no shared bearer token. Every request is authenticated by the provider's own signature over the unchanged raw body, checked against the secret belonging to the organizer the URL or the message tag names. Nothing is written before that check passes.
- Resend β Svix HMAC-SHA256, plus a 300-second freshness window
- SES β AWS SNS message signature, with an exact topic pin
- Mailgun β HMAC-SHA256
- SMTP2GO β optional shared secret
π Configuration
One environment variable, because it is needed before any tenant is known:
DATABASE_URLβ resolves the sender, and writes the ledger
Everything else is per organizer, in AccessKey (RESEND_WEBHOOK_SECRET) and in the organizer's business-rules settings (emails.ses.*). Neither can be asserted globally, which is why /api/health does not try.
π Where the detail lives
API-EMAIL.mdβ the full write-up, rendered by the docs app at/email-apipackages/email/CLAUDE.mdβ driver contract and per-driver specificsdocs/amazon-ses-campaign-setup.mdβ SES and SNS provisioning