Transactional email
Resend + react-email templates, dispatched through jobs.
Transactional email pairs Resend with
react-email templates, sent asynchronously through the
jobs feature. Product code never calls the transport directly: it enqueues an
email/send event with a registry-checked template name and typed props, and
the job renders and delivers it — with an idempotency ledger guaranteeing at
most one delivery per key.
Optional feature
Email is optional (core: false, requires: ["jobs"]). When it's pruned,
auth/orgs' marked send sites fall back to a console transport, so those flows
keep working — just logged instead of delivered.
What it does
- Public surface —
enqueueEmail({ to, template, props, idempotencyKey })dispatches anemail/sendevent;emailIdempotencyKey(template, seed)derives a stable, per-send key so a retried mutation can't double-send. - Template registry — typed react-email templates (
emails/registry.tsx,layout.tsx) so a template name and its props are checked at compile time. - Send job — the
email/sendInngest function renders the template and calls Resend, recording the key in the dedup ledger first. - Previews —
email:devruns react-email's preview server overfeatures/email/previews/*.
Models it adds
| Model | Purpose |
|---|---|
EmailDispatch | The email/send idempotency ledger — a unique idempotencyKey per delivery, so a replayed event sends nothing. |
It's a dedup ledger, not an inspection log: it stores the key plus enough to debug a collision, not message bodies.
Env it needs
RESEND_API_KEY= # Resend API key
EMAIL_FROM= # default From addressAdds the email:dev script (react-email preview server).
Composition
requires: ["jobs"]— sends flow through theemail/sendevent.- auth and orgs are soft consumers: verification, magic-link, reset,
and org-invite templates are enqueued at
@feature:email-marked call sites, with a console fallback when email is absent. (The auth/orgsemails/directories are owned by the email feature and prune with it.) - billing
requires: ["email"]— receipt and payment-failed emails. - usage sends a quota-warned email through the injected email surface.
What pruning removes
Leaving email out drops features/email, the auth/emails and orgs/emails
template dirs, the EmailDispatch model, and the email/send job. Marked
call sites in auth/orgs revert to the console transport. Because billing
requires email, deselecting email also deselects billing (and its dependents
flags, usage, admin, marketing).