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.
- 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.
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
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
| Provider | Cost per 1,000 | 50K 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.
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.
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.
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);
}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.
| Provider | 50K/mo | 250K/mo | 1M/mo | Contacts |
|---|---|---|---|---|
| AWS SES (direct) | $5 | $25 | $100 | N/A |
| Wraps + SES | $24 | $104 | $299 | Unlimited |
| Resend | $20 | $225 | $900 | N/A |
| SendGrid | $20 | $250 | $900 | Tiered |
| Customer.io | $100+ | $100+ | $100+ | Per-profile |
| Mailchimp | N/A | $230+ | $800+ | Per-contact |
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.
-- 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
);
$$
);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");
});- 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
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.
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
# 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.

