Wraps Logo
Supabase + Email Guide

4 Email Flows Your Supabase App Needs Before Going Live

Supabase gets you to a working app fast. Database, auth, edge functions — all handled. But email beyond magic links? That's where things get messy.

12 min readWraps Team
4
Email flows to configure
$0.10
Per 1,000 emails on SES
2 min
Setup with Wraps CLI

Every production Supabase app needs four email flows. Supabase handles one of them (partially). The other three are completely on you. Here's what each flow requires, the common approaches, and the tradeoffs you'll hit.

1. Auth Emails

Magic links, password resets, confirmations

2. Transactional Emails

Welcome, notifications, receipts

3. Broadcasts

Newsletters, product updates, announcements

4. Automated Emails

Digests, drip sequences, usage alerts

Flow 1: Auth Emails

Magic links, password resets, signup confirmations. Supabase Auth handles these out of the box — but the defaults have real problems in production.

What Supabase Gives You

Supabase Auth sends emails automatically when users sign up, reset passwords, or request magic links. Zero configuration needed to get started.

The Problem With Defaults
  • Emails send from Supabase's shared domain — Gmail and Outlook flag these more aggressively than custom domains
  • Default templates are plain text and look like phishing emails to your users
  • Built-in SMTP rate limits are low (as few as 2-3 emails/hour) and can change without notice
  • No deliverability visibility — you can't see if emails are landing in spam

The Fix: Custom SMTP

Supabase lets you configure a custom SMTP provider in Dashboard → Authentication → SMTP Settings. This means your auth emails send from your domain, with your branding, through your infrastructure.

Supabase SMTP Settings
Host:     email-smtp.us-east-1.amazonaws.com
Port:     587
Username: AKIAIOSFODNN7EXAMPLE      # SES SMTP credentials
Password: wJalrXUtnFEMI/K7MDENG...  # (not your AWS secret key)
Sender:   noreply@yourdomain.com
Before You Go Live

Verify your sending domain with SPF, DKIM, and DMARC records. Without these, even custom SMTP emails can land in spam. Wraps handles all three automatically during setup.

Cost Comparison for Auth Emails

ProviderCost per 1,00050K emails/mo
AWS SES$0.10$5
Resend$0.40$20
SendGrid$0.40$20
Postmark$1.20$66

Auth emails alone probably won't break the bank. But this is just one of four flows — the costs add up.

Flow 2: Transactional Emails

Welcome emails, comment notifications, payment receipts, order confirmations. These bring users back to your app — and Supabase Auth doesn't handle any of them.

The Biggest Gotcha
Auth emails and transactional emails are completely separate systems in Supabase. Most developers don't realize this until they're already in production and users aren't getting welcome emails.

Option A: Edge Functions + fetch

Call an email API from a Supabase Edge Function. Works, but you're building email infrastructure from scratch — no templates, no tracking, no retry logic.

supabase/functions/send-welcome/index.ts
import { serve } from "https://deno.land/std/http/server.ts";

serve(async (req) => {
  const { email, name } = await req.json();

  // Raw fetch to an email API
  const res = await fetch("https://api.resend.com/emails", {
    method: "POST",
    headers: {
      Authorization: "Bearer re_xxxxx",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      from: "hello@yourdomain.com",
      to: email,
      subject: "Welcome to the app",
      html: `<p>Hey ${name}, welcome aboard!</p>`,
    }),
  });

  return new Response(JSON.stringify({ ok: res.ok }));
});

This works for a single email. But you'll need to add error handling, retry logic, template management, and tracking yourself. That's a lot of code for "send a welcome email."

Option B: Next.js API Route + Email SDK

If your frontend is Next.js, you can send from an API route instead. This gives you better error handling and keeps email logic close to your application code.

app/api/welcome/route.ts
import { WrapsEmail } from "@wraps.dev/email";

const email = new WrapsEmail();

export async function POST(req: Request) {
  const { to, name } = await req.json();

  const result = await email.send({
    from: "hello@yourdomain.com",
    to,
    subject: "Welcome to the app",
    html: `<p>Hey ${name}, welcome aboard!</p>`,
  });

  return Response.json(result);
}
Why This Is Better

The Wraps SDK resolves AWS credentials automatically (no API keys to manage), handles retries, and sends through SES in your AWS account. You own the sending infrastructure.

Option C: Database Webhooks

Trigger emails based on database changes. When a row is inserted into your orders table, fire a webhook that sends a confirmation email. More robust than manual API calls, but you can't preview what goes out and debugging is harder.

Flow 3: Broadcasts

Newsletters, product updates, announcements. One-time sends to a group of users. Supabase doesn't support this at all — and the workarounds all have significant tradeoffs.

The Common Approaches

Export users to CSV, import to Mailchimp

Manual, gets stale instantly, creates duplicate contacts across systems. Works once. Doesn't scale.

Sync users to a third-party via webhooks

More robust, but now you're maintaining user sync code, doing a one-time backfill, and paying per-contact pricing on the other end.

Build it yourself with Edge Functions

Query users, batch send, handle rate limiting, manage unsubscribes, comply with CAN-SPAM. That's a product, not a feature.

The Real Problem: Contact-Based Pricing

Most broadcast tools charge by the number of contacts you store. Your Supabase auth.users table grows, and your email bill grows with it — even if half those users are inactive.

Provider50K/mo250K/mo1M/mo
AWS SES (direct)$5$25$100
Wraps + SES$24$104$299
Resend$20$225$900
SendGrid$20$250$900
Customer.io$100+$100+$100+
MailchimpN/A$230+$800+

Wraps + SES = Wraps platform fee + AWS SES sending cost. All tiers include unlimited contacts.

Flow 4: Automated Emails

Weekly digests, onboarding drip sequences, usage alerts, renewal reminders. The hardest flow to get right — and the most dangerous to get wrong, since you're sending to everyone without seeing the email first.

The pg_cron Approach

Most Supabase developers wire up a pg_cron job that calls an Edge Function on a schedule. It works, but has real limitations.

pg_cron job setup
-- Run every Monday at 9am UTC
SELECT cron.schedule(
  'weekly-digest',
  '0 9 * * 1',
  $$
  SELECT net.http_post(
    url := 'https://your-project.supabase.co/functions/v1/weekly-digest',
    headers := '{"Authorization": "Bearer YOUR_SERVICE_KEY"}'::jsonb
  );
  $$
);
supabase/functions/weekly-digest/index.ts
import { createClient } from "@supabase/supabase-js";

serve(async () => {
  const supabase = createClient(
    Deno.env.get("SUPABASE_URL")!,
    Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
  );

  // Get all active users
  const { data: users } = await supabase
    .from("profiles")
    .select("email, name")
    .eq("digest_enabled", true);

  // Send to each user... but what does the email look like?
  // No preview. No visual builder. Hope for the best.
  for (const user of users ?? []) {
    await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: { Authorization: "Bearer re_xxxxx" },
      body: JSON.stringify({
        from: "digest@yourdomain.com",
        to: user.email,
        subject: "Your weekly digest",
        html: buildDigestHtml(user), // hand-rolled HTML
      }),
    });
  }

  return new Response("ok");
});
What Can Go Wrong
  • No preview — you can't see what gets sent before it goes to all users
  • No visual builder — you're writing HTML strings in code
  • No error recovery — if the function crashes mid-send, some users get the email and some don't
  • No send-time personalization — everyone gets the same content unless you build per-user logic
  • Edge Function timeout — long user lists can exceed the execution limit (150s free / 400s paid)

The Alternative: Visual Workflows

A dedicated email platform with a workflow builder lets you design automated sequences visually — with triggers, delays, conditions, and preview. You see exactly what gets sent before it goes out.

What a workflow looks like

Trigger
user.signed_up
Wait
1 day
Send
Welcome email
Wait for
first_send event
Branch
activated?

The Full Stack: Supabase + Wraps + Your AWS

Here's how all four flows come together. Supabase handles auth and database. Wraps handles email as a platform. Everything sends through SES in your AWS account.

Supabase
Auth + Database
Wraps
Templates + Broadcasts + Workflows
Your AWS
SES + DynamoDB + Lambda

What You Own

  • SES configuration and sending reputation
  • Email event data in your DynamoDB
  • Domain verification (DKIM, SPF, DMARC)
  • Everything stays if you leave Wraps

What Wraps Adds

  • Template builder with AI generation
  • Segments, broadcasts, and scheduling
  • Visual workflow builder with automations
  • TypeScript SDK with auto credential resolution

Setup: 3 Steps

Terminal
# 1. Install the CLI
npm install -g @wraps.dev/cli

# 2. Deploy email infrastructure to your AWS
wraps email deploy

# 3. Send your first email
wraps email send --to user@example.com --subject "Hello from Wraps"

The CLI deploys SES, DynamoDB, Lambda, and IAM resources to your AWS account. DKIM, SPF, and DMARC are configured automatically. Takes about 2 minutes.

FAQ

Yes. Resend is a solid choice for transactional email. The tradeoffs: you start at $20/mo for 50K emails (vs ~$5 on SES), overages are $0.90/1K, you don't own the sending infrastructure, and you'll need a separate tool for broadcasts and automations. If you're sending under 10K emails/month and only need transactional, Resend is fine. At scale or if you need a full platform, the economics favor SES.

TL;DR

Auth emails — Supabase handles this, but configure custom SMTP before launch. Default sender domain hurts deliverability.

Transactional emails — Completely separate from auth. You need an email service. Use an SDK in your Next.js API routes for the best DX.

Broadcasts — Not supported by Supabase. Third-party tools charge per contact. Look for unlimited contacts or you'll pay for inactive users.

Automated emails — pg_cron works but has no preview, no visual builder, and timeout risks. A workflow builder is safer for production.

If you're on AWS (or open to it), SES at $0.10/1K is 4-12x cheaper than managed providers. The question isn't whether to set up email infrastructure — it's whether you want to rent it or own it.

Get Production Email Running in 2 Minutes

Wraps deploys SES, templates, broadcasts, and workflows to your AWS account. Unlimited contacts on every plan.

Related Articles