How to Send Emails from Supabase (2026 Guide)

Supabase provides a Postgres database, auth, and Edge Functions (Deno-based serverless functions). Edge Functions are where you send emails: they run server-side, can access secrets, and respond to HTTP requests or database events.
This guide covers Edge Functions for email sending, database webhook triggers, and auth event handlers.
Create an Edge Function
supabase functions new send-emailThis creates supabase/functions/send-email/index.ts.
Email Sending Function
Deno.serve(async (req) => {
if (req.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const { to, subject, body } = await req.json();
if (!to || !subject || !body) {
return Response.json({ error: "to, subject, and body required" }, { status: 400 });
}
const response = await fetch("https://api.sequenzy.com/v1/transactional/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${Deno.env.get("SEQUENZY_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ to, subject, body }),
});
if (!response.ok) {
return Response.json({ error: "Failed to send" }, { status: 500 });
}
const result = await response.json();
return Response.json(result);
});Deno.serve(async (req) => {
if (req.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const { to, subject, html } = await req.json();
if (!to || !subject || !html) {
return Response.json({ error: "to, subject, and html required" }, { status: 400 });
}
const response = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${Deno.env.get("RESEND_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "Your App <noreply@yourdomain.com>",
to, subject, html,
}),
});
if (!response.ok) {
return Response.json({ error: "Failed to send" }, { status: 500 });
}
const result = await response.json();
return Response.json(result);
});Deno.serve(async (req) => {
if (req.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const { to, subject, html } = await req.json();
if (!to || !subject || !html) {
return Response.json({ error: "to, subject, and html required" }, { status: 400 });
}
const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${Deno.env.get("SENDGRID_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
personalizations: [{ to: [{ email: to }] }],
from: { email: "noreply@yourdomain.com" },
subject,
content: [{ type: "text/html", value: html }],
}),
});
if (!response.ok) {
return Response.json({ error: "Failed to send" }, { status: 500 });
}
return Response.json({ sent: true });
});Set Secrets
supabase secrets set SEQUENZY_API_KEY=sq_your_keyDeploy
supabase functions deploy send-emailCall from Your App
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
// Call the edge function
const { data, error } = await supabase.functions.invoke("send-email", {
body: {
to: "user@example.com",
subject: "Welcome!",
body: "<h1>Welcome!</h1><p>Your account is ready.</p>",
},
});Database Webhook Trigger
Send an email when a new row is inserted. Create a webhook function:
// supabase/functions/on-new-order/index.ts
Deno.serve(async (req) => {
const payload = await req.json();
const { record } = payload; // The new row
const response = await fetch("https://api.sequenzy.com/v1/transactional/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${Deno.env.get("SEQUENZY_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
to: record.email,
subject: "Order confirmed!",
body: `<h1>Thanks for your order!</h1><p>Order #${record.id} is confirmed.</p>`,
}),
});
return Response.json({ sent: response.ok });
});Then in the Supabase dashboard: Database > Webhooks > Create webhook pointing to this function, triggered on INSERT to your orders table.
Auth Email Trigger
Send a welcome email when a user signs up:
// supabase/functions/on-auth-signup/index.ts
Deno.serve(async (req) => {
const payload = await req.json();
const { email, user_metadata } = payload.record;
await fetch("https://api.sequenzy.com/v1/transactional/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${Deno.env.get("SEQUENZY_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
to: email,
subject: `Welcome, ${user_metadata?.name ?? "there"}!`,
body: `<h1>Welcome!</h1><p>Your account is ready.</p>`,
}),
});
return Response.json({ sent: true });
});Going to Production
1. Verify Your Domain
Add SPF, DKIM, DMARC DNS records.
2. Use Secrets, Not Env Vars
supabase secrets set SEQUENZY_API_KEY=sq_your_key3. Add Error Handling
Edge Functions have a 60-second timeout. Email API calls are fast, but add error handling for reliability.
Beyond Transactional
Sequenzy handles transactional sends, marketing campaigns, automated sequences, and subscriber management from one API. Native Stripe integration for SaaS.
Wrapping Up
- Edge Functions for server-side email sending
- Database webhooks to trigger emails on row changes
- Auth triggers for welcome emails on signup
- Supabase client to invoke functions from your app
Pick your provider, deploy a function, and start sending.