Supabase is an incredible stack. Auth, Postgres, storage, edge functions, realtime, vector: all in one place. You can ship a real product in a weekend.
Then you need to send an email. And you discover the missing piece.
Supabase lets you configure email (SMTP for auth) but it deliberately doesn't send marketing or lifecycle email. The auth.email layer is locked to 2 emails per hour on the free plan, and even on Pro it's capped hard and only covers auth events. The moment you need a welcome sequence, a trial-expiring reminder, a payment-failed nudge, or a re-engagement campaign, you're on your own.
This post is about what happens next. I'll walk through the three most common workarounds I see in the wild for Supabase builders, explain exactly where each one breaks, and show what a clean DB-native approach looks like.
Why Supabase doesn't ship a full email layer

Supabase's team has said this publicly: they want to be the best backend, not a marketing tool. It's the right call for them: email deliverability, compliance, bounces, reputation, unsubscribe flows, template rendering, A/B testing, segmentation: that's a full product, not a feature.
The side effect is that every Supabase SaaS has to bolt on email separately. And the ways people do it today range from "works but expensive" to "works until it silently breaks."
The hard limit you'll hit first
The Supabase auth SMTP is rate-limited:
| Plan | Auth emails / hour | Covers |
|---|---|---|
| Free | 2 | Signup, password reset, magic link |
| Pro | ~30 | Auth events only |
| Custom SMTP | Unlimited* | What your provider allows |
The second row is where most solo founders hit the wall. You launch on Reddit, 400 people sign up in an hour, and 370 of them never receive the welcome link because you blew past the cap. You think the problem is "Supabase is slow today." It's not. You needed a different tool yesterday.
Workaround 1: Resend + custom webhooks
The most popular DIY stack. You buy Resend (or Postmark, SendGrid), you write an edge function that listens for DB changes or API calls, and you call the email API from there.
What it looks like
// supabase/functions/send-welcome/index.ts
import { Resend } from "npm:resend"
const resend = new Resend(Deno.env.get("RESEND_API_KEY"))
Deno.serve(async (req) => {
const { record } = await req.json()
await resend.emails.send({
from: "hello@yourapp.com",
to: record.email,
subject: "Welcome to YourApp",
html: `<p>Hey ${record.first_name}, welcome!</p>`,
})
return new Response("ok")
})
Plus a database trigger or webhook that fires the function when users gets a new row.
Where it breaks
Three places, in order of when they bite you:
- The HTML lives in your code. Changing a comma in the welcome email requires a code change, a PR, and a redeploy. Your non-technical cofounder can't touch it. Your designer can't preview it. Your Claude Code agent can edit it, but now you have a 4-line diff on every copy tweak that rots your git log.
- The second email kills you. Welcome email is easy. Now you need: trial expiring at T-3 days, payment failed retry, feature nudge, weekly digest. Each one is a new edge function, a new trigger, new state to track (did we already send it?), new idempotency logic. You end up with 12 edge functions and a Google Sheet that tracks which users got which email. This is how 3 months of runway disappears.
- There's no view on the user side. You have no idea which users have received which messages, which ones bounced, which ones clicked. You learn about problems when users tell you on Discord. Resend has a dashboard, but it's grouped by send-batch, not by user: so "did this user get our onboarding?" is a question you can't answer without a spreadsheet.
This approach is fine for transactional-only (reset password, receipts). It falls apart the moment you need lifecycle.
Workaround 2: Customer.io with reverse ETL

The grown-up version. You keep Customer.io (or Braze, or Iterable) as your messaging platform and you pipe your Supabase data out to it via a reverse ETL tool like Hightouch or Census.
What it looks like
Supabase (Postgres) → Hightouch sync every 15 min → Customer.io
↓
Email, push, in-app
You define models in Hightouch (users_active, trial_expiring), you schedule syncs, Customer.io receives the users as "people" objects, and you build flows in their UI.
Where it breaks
It actually works. The problem is the cost and the latency.
Cost: Customer.io starts at around $100/mo with a low limit. Hightouch free tier is tiny, then jumps to $450+/mo. You're looking at ~$600/mo before you have 100 paying users. For a bootstrapped SaaS this is brutal.
Latency: The cheapest Hightouch syncs run every 15 minutes. That means a user signs up at 10:00, the sync runs at 10:15, Customer.io evaluates the audience at 10:15:30, the "welcome" message goes at 10:16. Sixteen minutes after signup is dead time: the user has either already activated or bounced. You can pay more for faster syncs, but now you're at $800+/mo and you still have a sync instead of a real trigger.
Data duplication: Your user data now lives in two places. Every schema change in Supabase means updating a Hightouch model. Every privacy request means deleting from both systems. Every debugging session starts with "is this a Supabase problem or a Customer.io problem?"
This stack works great at $2M ARR. It's wrong for vibe-coded SaaS at $0-$100k ARR.
Workaround 3: Mailchimp + CSV imports
Don't. Please don't.
I'm including it because I see it every week. Someone exports users from Supabase to a CSV, uploads to Mailchimp, sends a broadcast, and calls it "marketing automation."
It's not. It's a mailing list from 2010. There's no trigger, no personalization beyond first name, no link to database events, and you're breaking GDPR the moment a user who deleted their account in your app still receives your monthly update because you forgot to re-export.
If you're doing this, you'd get better results sending from Gmail manually. At least you'd notice.
What the missing piece actually looks like
The common thread in all three workarounds is that they move data away from the database. They sync, they copy, they export. And every copy is a new place where things can rot.
The approach that works for Supabase SaaS is the opposite: the email layer reads the database directly, listens to changes in real time, and triggers messages without ever moving data out.
Three things that have to be true
- Real-time triggers, not polling. When a row in
userschanges, the email layer knows within milliseconds: not 15 minutes later. Supabase already has realtime. Use it. - Content lives outside the code. The welcome email has a stable ID (
welcome_v1). Your backend calls "send welcome_v1 to this user." The content is a template in a visual editor, editable without touching code. Copy changes don't need deploys. - Users are users, not "people." The email platform shows you your actual Supabase users with their real columns (
trial_ends_at,onboarding_step,plan). Not a reverse-ETL shadow copy with 15-minute lag.
The stack that delivers this
Minimo is built around this approach. It connects read-only to your Supabase project, listens to realtime changes, stores messaging state (delivered, opened, bounced, etc.) in its own DB: and never asks you to sync your users out.
// Your backend, after signup
await minimo.send({
template: "welcome_v1", // stable UID
to: { user_id: newUser.id }, // Minimo resolves from your Supabase
})
That's it. No webhooks to maintain. No Hightouch bill. No CSV imports. You edit the welcome email in Minimo's visual editor and the next send uses the new version: no code change, no deploy.
And when you're ready to add a trial-expiring reminder, you don't write a new edge function. You open the automation builder, drag a trigger node ("when trial_ends_at is in 3 days"), connect it to a delay, connect it to a "send template" action. Ten minutes of clicking, same source of truth, same user objects.
The takeaway
Supabase deliberately leaves email out, and that's correct for them. But the gap leaves every Supabase SaaS with a choice between a DIY stack that breaks at scale, an enterprise stack that costs $600+/mo before you have revenue, or a CSV workflow that shouldn't exist in 2026.
The missing piece is a messaging layer that treats your database as the source of truth instead of something to be copied. That's what we've been building.
If you're in the middle of the Resend + edge functions loop right now and you can feel it eating your weekends: you don't have to keep doing that. Start a free Minimo account and connect your Supabase project. It takes about five minutes, and the first welcome email you send won't need a redeploy.
Further reading
- Supabase auth email rate limits
- Supabase realtime concepts
- The Minimo pricing page: WhatsApp is included on Growth, for when email alone isn't enough



