Email

indiegoon supports 5 email providers through a unified interface. Like payments, switch providers by changing one env var.

Supported Providers

ProviderBest ForSetup Difficulty
ResendModern apps, great DXEasy
PostmarkHigh deliverabilityEasy
SendGridVolume sendersMedium
MailgunDevelopers, EU complianceMedium
Nodemailer (SMTP)Gmail, SES, self-hostedEasy

Setup

Using the CLI

goon setup email

Pick your provider. The CLI validates your API key, sets the from address, and configures everything.

Manual Setup

# Choose: resend | postmark | sendgrid | mailgun | nodemailer
EMAIL_PROVIDER="resend"

# Resend
RESEND_API_KEY="re_..."

# Common
EMAIL_FROM_NAME="My SaaS"
EMAIL_FROM_ADDRESS="hello@mysaas.com"
EMAIL_REPLY_TO="support@mysaas.com"

Sending Emails

Basic Usage

import { sendEmail } from "@/lib/email"

await sendEmail({
  to: "user@example.com",
  subject: "Welcome to My SaaS",
  html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
})

Using Templates

Auth email templates are pre-built:

import { sendEmail } from "@/lib/email"
import {
  welcomeEmail,
  verifyEmailTemplate,
  passwordResetEmail,
  magicLinkEmail,
} from "@/lib/email/templates/auth"

// Welcome email (sent automatically on signup)
await sendEmail({
  to: user.email,
  subject: "Welcome to My SaaS!",
  html: welcomeEmail({ name: user.name }),
})

// Verification email
await sendEmail({
  to: user.email,
  subject: "Verify your email",
  html: verifyEmailTemplate({ url: verificationUrl, name: user.name }),
})

Creating Custom Templates

Add new templates in src/lib/email/templates/:

// src/lib/email/templates/billing.ts

export function invoiceEmail({ amount, date }: { amount: string; date: string }) {
  return `
    <div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
      <h1>Invoice Paid</h1>
      <p>Your payment of <strong>${amount}</strong> on ${date} was successful.</p>
      <a href="https://mysaas.com/billing" 
         style="background: #f59e0b; color: #000; padding: 12px 24px; text-decoration: none; border-radius: 6px;">
        View Invoice
      </a>
    </div>
  `
}

Provider Details

Resend

EMAIL_PROVIDER="resend"
RESEND_API_KEY="re_..."

Uses the official Resend SDK. Best developer experience.

Postmark

EMAIL_PROVIDER="postmark"
POSTMARK_SERVER_TOKEN="..."

REST API. Known for excellent deliverability.

SendGrid

EMAIL_PROVIDER="sendgrid"
SENDGRID_API_KEY="SG...."

REST API. Good for high-volume sending.

Mailgun

EMAIL_PROVIDER="mailgun"
MAILGUN_API_KEY="key-..."
MAILGUN_DOMAIN="mg.mysaas.com"
MAILGUN_REGION="us"  # or "eu"

REST API with Basic auth. Supports EU region for GDPR.

Nodemailer (SMTP)

EMAIL_PROVIDER="nodemailer"
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_USER="your-email@gmail.com"
SMTP_PASS="your-app-password"
SMTP_SECURE="false"

Works with Gmail (App Password), Amazon SES, Mailtrap, or any SMTP server.

How It's Wired

Auth emails are sent automatically:

  • Signup → Welcome email
  • Email verification → Verification link
  • Forgot password → Reset link
  • Magic link sign-in → Magic link

This is configured in the auth hooks at src/lib/auth/index.ts. You don't need to wire anything manually.

Testing Emails

Development

Use Mailtrap or Ethereal to catch emails without sending to real inboxes:

EMAIL_PROVIDER="nodemailer"
SMTP_HOST="sandbox.smtp.mailtrap.io"
SMTP_PORT="2525"
SMTP_USER="your-mailtrap-user"
SMTP_PASS="your-mailtrap-pass"

Resend Test Mode

Resend delivers to verified domains only in test mode. Send to your own email during development.

Next Steps

  • Teams — Multi-tenant workspaces with invitations
  • Admin — User management and subscription monitoring