Skip to main content

Email

How rekkal sends transactional email with Resend and authors templates with React Email.

Environment variables

Email reads two variables (listed in .env.example):
  • RESEND_API_KEY — the Resend API key. Server-only; never import it into a "use client" module.
  • RESEND_FROM — the from address (e.g. "Acme <hello@acme.com>"). Must be on a verified domain in production; falls back to Resend’s sandbox sender when unset.

The client — lib/resend.ts

lib/resend.ts exports getResend(), a lazily-constructed client (mirroring getStripe()). Construction is deferred because the Resend SDK throws when the key is absent — so the app still builds and runs without email configured, and callers gate sends on the key being present.

Sending — lib/email.ts

Don’t call getResend() directly from feature code — use the helpers in lib/email.ts: sendWelcomeEmail(...) and sendSubscriptionActiveEmail(...). Each one:
  • No-ops when email is not configured (RESEND_API_KEY absent or the email feature flag off), so local dev and CI never error.
  • Is best-effort and non-throwing — the Resend SDK returns { data, error } (it does not throw); errors are logged, never propagated, so a mail failure can’t break signup or a webhook.
  • Passes an idempotency key (welcome-email/<userId>, subscription-active/<subscriptionId>) so a retry never sends twice.
sendWelcomeEmail is driven by sendWelcomeIfNeeded in lib/welcome.ts, which sends at most once per user by checking profiles.welcomed_at. It runs from /auth/callback after the code exchange, and from the sign-up action only when Supabase returns a session outright (email confirmation disabled) — never on the unconfirmed path, which is unauthenticated and would let anyone mail an address they don’t control. sendSubscriptionActiveEmail is sent from the Stripe webhook on a successful checkout.

Templates — emails/

Email templates are React components under emails/, authored with React Email primitives, and passed to Resend via the SDK’s react option (which renders both HTML and plain text). The starter ships emails/welcome-email.tsx and emails/subscription-active-email.tsx. Add new templates as sibling files and send them from a helper in lib/email.ts.

Further reading

The resend skill is pre-bundled (see agent-skills.md) and carries gotchas — idempotency keys, webhook verification, template variable syntax — worth reading before sending in production.