Supabase guide
Send with Pigeon from a Supabase Edge Function. The service role stays on Supabase. The Pigeon key stays in function secrets.
1. Prepare Pigeon
In the dashboard, verify the domain you will put in From. Create a sending API key. Copy the key once. You will store it as PIGEON_API_KEY, not in git.
2. Set function secrets
In the Supabase project, open Edge Functions, then Secrets. Add these three. For local Pigeon, PIGEON_URL is your machine, for example http://host.docker.internal:4005. For a deployed Pigeon host, use https://your-pigeon-host with no trailing slash.
supabase secrets set \ PIGEON_URL=http://host.docker.internal:4005 \ PIGEON_API_KEY=pg_your_key \ PIGEON_FROM="Ada <ada@yourdomain.com>"
3. Deploy the function
Create supabase/functions/welcome/index.ts (a copy lives in examples/supabase/welcome). Deploy it, then point a Database Webhook at that function when a row is inserted into auth.users or your profiles table.
supabase functions deploy welcome --no-verify-jwt
--no-verify-jwt is only for a database webhook that cannot send a user JWT. Keep the function private. Do not expose it on the public internet without a shared secret header you check in the handler.
4. Send on insert
The webhook body includes record.email. The function posts that address to Pigeon. Create a template named welcome in Pigeon, or send html instead of template.
import "jsr:@supabase/functions-js/edge-runtime.d.ts"; Deno.serve(async (req) => { const payload = await req.json(); const email = payload.record?.email; if (!email) return new Response("missing email", { status: 400 }); const response = await fetch(Deno.env.get("PIGEON_URL") + "/api/emails", { method: "POST", headers: { Authorization: `Bearer ${Deno.env.get("PIGEON_API_KEY")}`, "Content-Type": "application/json" }, body: JSON.stringify({ from: Deno.env.get("PIGEON_FROM"), to: [email], template: "welcome", variables: { email } }) }); return new Response(await response.text(), { status: response.status }); });
5. Check delivery
Open Pigeon Emails. A welcome send should show as sent or delivered. Without SES credentials it lands in /dev/mailbox. A 401 means the key is wrong. A 403 domain_not_verified means From does not match a verified domain.