TrademarkTrademark
Slices

Email

Transactional email on Resend, starting with organization invitations.
The email slice sends transactional email through Resend. No surface requires it, because an email provider is a recurring cost and a choice the app makes, so it is asked for by name: It adds no dependency. src/lib/email/send.ts calls Resend's REST API over global fetch, the same way billing calls Stripe.

What it sends

  • Organization invitations. With the orgs slice (the dashboard brings it in), inviting someone emails them the link. The invite dialog says "Invitation sent" and still shows the link to copy. A retried request sends once: the invitation id is the Resend idempotency key.
Anything else is one call: sendEmail returns a result instead of throwing, so a failed email never fails the request that caused it.

Setup

  • Create a Resend account and verify your sending domain.
  • Set RESEND_API_KEY (an API key with sending access) and EMAIL_FROM (for example Acme <hello@acme.com>, on the verified domain).
Without RESEND_API_KEY nothing is sent and invitations stay copy-the-link, so the app builds and runs with no environment at all. Without EMAIL_FROM the sender is Resend's test address onboarding@resend.dev, which delivers only to your own Resend account's email: enough to try it, not to ship.

Supabase auth emails

Sign-up confirmations and password resets are sent by Supabase, not by this code. Supabase's built-in mailer is rate-limited and meant for development, so production apps need their own SMTP. Point it at Resend with the same key, in the Supabase dashboard under Authentication, then Emails, then SMTP Settings:
  • Host smtp.resend.com, port 465
  • Username resend, password your RESEND_API_KEY
  • Sender email: the EMAIL_FROM address
Then every email the app sends comes from one domain and one provider.
Last updated October 23, 2026
Edit this page on GitHub