Skip to main content

Overview

Featul uses Resend as the email service provider for:
  • Email verification (OTP codes)
  • Welcome emails
  • Password reset emails
  • Workspace invitations
  • Reservation confirmations
  • Report notifications

Resend Setup

1. Create Resend Account

  1. Sign up at Resend
  2. Verify your email address
  3. Add and verify your sending domain

2. Domain Verification

  1. Go to Resend Dashboard > Domains
  2. Add your domain (e.g., featul.com)
  3. Add the provided DNS records:
    • SPF record (TXT)
    • DKIM record (TXT)
    • DMARC record (TXT)
  4. Wait for verification (usually a few minutes)
For development, you can use Resend’s test mode which allows sending to verified email addresses without domain verification.

3. Generate API Key

  1. Go to Resend Dashboard > API Keys
  2. Create a new API key with sending permissions
  3. Copy the API key (you won’t be able to see it again)

4. Environment Variables

Add these to your .env file:
.env
RESEND_API_KEY=re_your_api_key_here
RESEND_FROM=featul <no-reply@featul.com>
  • RESEND_API_KEY: Your Resend API key
  • RESEND_FROM: The “From” address and name for outgoing emails
Configuration in code: packages/auth/src/email/transport.ts:9-10
Make sure the domain in RESEND_FROM matches your verified domain in Resend.

Email Types

Welcome Email

Sent automatically when a new user signs up:
await sendWelcome(to, name)
Code reference: packages/auth/src/email.ts:10-13 Triggered by: packages/auth/src/auth.ts:252-261

Email Verification (OTP)

Sent for:
  • Email verification during sign-up
  • Sign-in verification (if enabled)
  • Password reset
await sendVerificationOtpEmail(email, otp, type)
Code reference: packages/auth/src/email.ts:15-19 Triggered by: packages/auth/src/auth.ts:232-237
OTP codes are 6-digit numeric codes that expire after a short time (configured in better-auth).

Workspace Invitation

Sent when a user is invited to join a workspace:
await sendWorkspaceInvite(to, workspaceName, inviteUrl)
Code reference: packages/auth/src/email.ts:21-25

Reservation Email

Sent to confirm workspace subdomain reservations:
await sendReservationEmail(to, slug, confirmUrl)
Code reference: packages/auth/src/email.ts:27-31

Report Email

Sent when content is reported:
await sendReportEmail(to, props)
Code reference: packages/auth/src/email.ts:32-36

Email Transport

The email transport implementation uses the Resend API:
const res = await fetch("https://api.resend.com/emails", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ from, to, subject, html, text }),
})
Code reference: packages/auth/src/email/transport.ts:8-34

Development Mode

If RESEND_API_KEY is not set, emails will be logged to the console instead:
[email:dev] to=user@example.com subject=Welcome to featul
This allows development without configuring a real email service. Code reference: packages/auth/src/email/transport.ts:12-15
In test mode (non-production with invalid credentials), failed emails will log warnings instead of throwing errors.

Email Templates

Email templates are React components rendered to HTML:
  • welcomeemail.tsx: Welcome email template
  • verifyemail.tsx: OTP verification email template
  • inviteemail.tsx: Workspace invitation template
  • reserveemail.tsx: Reservation confirmation template
  • reportemail.tsx: Report notification template
Templates support custom branding (logo, colors) for white-label deployments. Template location: packages/auth/src/email/

Email Rendering

Emails are rendered with both HTML and plain text versions:
const { html, text } = await renderWelcomeEmail(name, brand)
await sendEmail({ to, subject, html, text })
This ensures compatibility with all email clients.

Rate Limiting

Email sending is indirectly rate-limited through authentication endpoint rate limits:
  • Email verification: 5 requests per 60 seconds
  • Password reset: 3 requests per 5 minutes
Code reference: packages/auth/src/auth.ts:219-221 See the Authentication Configuration page for rate limit setup.

Alternative Email Providers

To use a different email provider (SendGrid, Mailgun, etc.):
  1. Modify packages/auth/src/email/transport.ts
  2. Replace the Resend API call with your provider’s API
  3. Update environment variables accordingly
Example for SendGrid:
const apiKey = process.env.SENDGRID_API_KEY

const res = await fetch("https://api.sendgrid.com/v3/mail/send", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    personalizations: [{ to: [{ email: to }] }],
    from: { email: from },
    subject,
    content: [
      { type: "text/html", value: html },
      { type: "text/plain", value: text },
    ],
  }),
})

Build docs developers (and LLMs) love