How to Send Email from Supabase
Send transactional email from Supabase Edge Functions using the SendPigeon SDK. Complete guide with Deno examples, secrets management, and local testing.
To send email from Supabase, create an Edge Function, import the SendPigeon SDK with the npm: prefix, and call pigeon.send(). Supabase Edge Functions run on Deno — TypeScript works natively with no build step.
Quick setup:
supabase functions new send-emailsupabase secrets set SENDPIGEON_API_KEY=sp_live_xxx- Import
npm:sendpigeonand callpigeon.send()
Best for: Welcome emails, password resets, notifications triggered by database changes.
Create an Edge Function
supabase functions new send-email
This creates supabase/functions/send-email/index.ts.
Set Your API Key
Store the API key as a secret:
supabase secrets set SENDPIGEON_API_KEY=sp_live_your_key_here
For local development, create supabase/functions/.env:
SENDPIGEON_API_KEY=sp_live_your_key_here
Send Email from a Supabase Edge Function
// supabase/functions/send-email/index.ts
import { SendPigeon } from "npm:sendpigeon";
const pigeon = new SendPigeon(Deno.env.get("SENDPIGEON_API_KEY")!);
Deno.serve(async (req) => {
if (req.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const { to, name } = await req.json();
const { data, error } = await pigeon.send({
from: "hello@yourdomain.com",
to,
subject: `Hey ${name}!`,
html: `<h1>Welcome</h1><p>Thanks for signing up.</p>`,
});
if (error) {
return Response.json({ error: error.message }, { status: error.status ?? 500 });
}
return Response.json({ emailId: data.id });
});
Supabase Edge Functions use Deno.serve() — the built-in Deno HTTP server. No framework needed.
Send Welcome Email on User Signup
Trigger an email when a new user signs up by setting up a Database Webhook on the auth.users table:
// supabase/functions/welcome-email/index.ts
import { SendPigeon } from "npm:sendpigeon";
const pigeon = new SendPigeon(Deno.env.get("SENDPIGEON_API_KEY")!);
Deno.serve(async (req) => {
const payload = await req.json();
// Database Webhook payload for auth.users
const { email, raw_user_meta_data } = payload.record;
const name = raw_user_meta_data?.name || "there";
const { error } = await pigeon.send({
from: "hello@yourdomain.com",
to: email,
subject: `Welcome, ${name}!`,
html: `
<h1>Welcome to our app!</h1>
<p>Your account is ready. Here's what to do next:</p>
<ul>
<li>Complete your profile</li>
<li>Explore the dashboard</li>
</ul>
`,
});
if (error) {
console.error("Welcome email failed:", error.message);
return Response.json({ error: error.message }, { status: 500 });
}
return Response.json({ success: true });
});
Send Email on Database Change
Use Supabase Database Webhooks to send an email when a row is inserted. Set up the webhook in the Supabase dashboard (Database > Webhooks) to call your Edge Function on INSERT.
// supabase/functions/order-confirmation/index.ts
import { SendPigeon } from "npm:sendpigeon";
const pigeon = new SendPigeon(Deno.env.get("SENDPIGEON_API_KEY")!);
Deno.serve(async (req) => {
const { record } = await req.json();
// record contains the inserted row
const { customer_email, customer_name, order_id, total } = record;
const { error } = await pigeon.send({
from: "orders@yourdomain.com",
to: customer_email,
subject: `Order #${order_id} confirmed`,
html: `
<h1>Order Confirmed</h1>
<p>Hey ${customer_name}, your order #${order_id} for $${total} has been placed.</p>
<p>We'll notify you when it ships.</p>
`,
});
if (error) {
console.error("Order email failed:", error.message);
}
return Response.json({ success: true });
});
CORS for Browser Requests
If calling your Edge Function from a browser, add CORS headers:
// supabase/functions/_shared/cors.ts
export const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
// supabase/functions/contact/index.ts
import { SendPigeon } from "npm:sendpigeon";
import { corsHeaders } from "../_shared/cors.ts";
const pigeon = new SendPigeon(Deno.env.get("SENDPIGEON_API_KEY")!);
Deno.serve(async (req) => {
// Handle CORS preflight
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
const { name, email, message } = await req.json();
const { error } = await pigeon.send({
from: "contact@yourdomain.com",
to: "you@yourdomain.com",
subject: `Contact form: ${name}`,
html: `<p><strong>From:</strong> ${name} (${email})</p><p>${message}</p>`,
replyTo: email,
});
if (error) {
return Response.json({ error: "Failed to send" }, {
status: 500,
headers: corsHeaders,
});
}
return Response.json({ success: true }, { headers: corsHeaders });
});
Deploy
supabase functions deploy send-email
Your function is live at:
https://<project-id>.supabase.co/functions/v1/send-email
Call it with your Supabase anon key:
curl -X POST https://<project-id>.supabase.co/functions/v1/send-email \
-H "Authorization: Bearer YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "user@example.com", "name": "Johan"}'
Test Locally
Run your function locally:
supabase start
supabase functions serve send-email
The function runs at http://localhost:54321/functions/v1/send-email.
To catch emails locally instead of sending them, use the SendPigeon CLI:
npx @sendpigeon-sdk/cli dev
View captured emails at localhost:4100. See the local email testing guide for full setup.
Frequently Asked Questions
What runtime do Supabase Edge Functions use?
Supabase Edge Functions run on the Deno runtime. TypeScript and JavaScript work natively — no transpilation or build step. The runtime supports Web Standard APIs, Deno APIs, and npm packages via the npm: import prefix.
Can I use npm packages in Supabase Edge Functions?
Yes. Use the npm: prefix in your import: import { SendPigeon } from "npm:sendpigeon". No node_modules or package.json required — Deno resolves and caches the package automatically.
How do I store secrets in Supabase?
Run supabase secrets set SENDPIGEON_API_KEY=your_key to store secrets for production. Access them with Deno.env.get("SENDPIGEON_API_KEY"). For local development, place them in supabase/functions/.env.
Can I send email when a database row is inserted?
Yes. Set up a Database Webhook in the Supabase dashboard (Database > Webhooks) to trigger your Edge Function on INSERT, UPDATE, or DELETE. The function receives the row data in the request body.
Does SendPigeon work with Deno?
Yes. The SendPigeon SDK uses the Fetch API, which Deno supports natively. Import with npm:sendpigeon and it works the same as in Node.js.
Next Steps
- Set up DKIM, SPF, and DMARC on your sending domain
- Review the email deliverability checklist
- Build templates with our free visual email builder
- Browse our email templates for ready-to-use HTML
Other Platforms
Deploying elsewhere? We have guides for:
See all framework guides.