Supabase Auth email with SuperSend TX
Send Supabase signup, magic link, password reset, and invite emails through SuperSend TX using the Send Email auth hook and an Edge Function.
For hook-based delivery (Edge Functions), use the HTTP API (POST /emails) from your handler — the same pattern Supabase documents for other ESPs.
When to use this
- You built on Supabase Auth (often via Lovable, Vite, or Next.js) and want branded mail on your domain
- You outgrew Supabase’s built-in SMTP limits or need delivery visibility in SuperSend TX
- You want one transactional provider for auth mail and product mail (receipts, alerts)
Architecture
- User triggers auth (signup, reset, magic link, etc.)
- Supabase Auth calls your Send Email hook (HTTPS Edge Function)
- The function verifies the webhook signature, builds the message, and calls
POST https://api.supersendtx.com/emails - Return
200with{}so Supabase knows the email was handled
Reference: Supabase Send Email Hook.
Prerequisites
- SuperSend TX account +
stx_…API key - Verified sending domain (or Sandbox for self-tests only — see Quickstart)
- Supabase project with the Supabase CLI installed
1. Create the Edge Function
supabase functions new send-emailsupabase/functions/send-email/index.ts:
import { Webhook } from 'https://esm.sh/standardwebhooks@1.0.0'
const hookSecret = (Deno.env.get('SEND_EMAIL_HOOK_SECRET') ?? '').replace(/^v1,whsec_/, '')
const apiKey = Deno.env.get('SUPERSENDTX_API_KEY')
const fromEmail = Deno.env.get('SUPERSENDTX_FROM_EMAIL') ?? 'noreply@yourdomain.com'
const projectRef = Deno.env.get('SUPABASE_PROJECT_REF')
const subjects: Record<string, string> = {
signup: 'Confirm your email',
recovery: 'Reset your password',
invite: 'You have been invited',
magiclink: 'Your sign-in link',
email_change: 'Confirm your new email',
email_change_new: 'Confirm your new email',
reauthentication: 'Your verification code',
}
function confirmationUrl(emailData: {
token_hash: string
email_action_type: string
redirect_to: string
}) {
const base = `https://${projectRef}.supabase.co/auth/v1/verify`
const params = new URLSearchParams({
token: emailData.token_hash,
type: emailData.email_action_type,
redirect_to: emailData.redirect_to,
})
return `${base}?${params.toString()}`
}
Deno.serve(async (req) => {
if (req.method !== 'POST') {
return new Response('method not allowed', { status: 405 })
}
if (!apiKey || !hookSecret || !projectRef) {
return new Response('server misconfigured', { status: 500 })
}
const payload = await req.text()
const headers = Object.fromEntries(req.headers)
try {
const wh = new Webhook(hookSecret)
const { user, email_data } = wh.verify(payload, headers) as {
user: { email: string }
email_data: {
token: string
token_hash: string
redirect_to: string
email_action_type: string
}
}
const action = email_data.email_action_type
const subject = subjects[action] ?? 'Notification'
const link = confirmationUrl(email_data)
const html =
action === 'reauthentication'
? `<p>Your verification code is <strong>${email_data.token}</strong></p>`
: `<p><a href="${link}">${subject}</a></p>`
const text =
action === 'reauthentication'
? `Your verification code is ${email_data.token}`
: `${subject}: ${link}`
const res = await fetch('https://api.supersendtx.com/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: fromEmail,
to: user.email,
subject,
html,
text,
}),
})
if (!res.ok) {
const body = await res.text()
throw new Error(`SuperSend TX ${res.status}: ${body}`)
}
} catch (error) {
console.error(error)
return new Response(JSON.stringify({ error: String(error) }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
})
}
return new Response(JSON.stringify({}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
})Customize subjects and HTML (or render React Email in the function) for production.
2. Set secrets and deploy
supabase/functions/.env (local) — do not commit:
SUPERSENDTX_API_KEY=stx_your_key_here
SUPERSENDTX_FROM_EMAIL=noreply@yourdomain.com
SUPABASE_PROJECT_REF=your-project-ref
SEND_EMAIL_HOOK_SECRET=v1,whsec_... # from Supabase dashboard after step 3supabase secrets set --env-file supabase/functions/.env
supabase functions deploy send-email --no-verify-jwtNote the deployed function URL (e.g. https://<project-ref>.supabase.co/functions/v1/send-email).
3. Enable the Send Email hook
Alternative: If you prefer Supabase’s built-in SMTP settings UI, create an SMTP credential in SuperSend TX (SMTP in the dashboard) and configure custom SMTP in Supabase with host smtp.supersendtx.com, port 587, username supersendtx, and your credential password. Skip the hook steps below.
In Supabase Dashboard → Authentication → Hooks:
- Add Send Email hook
- Type: HTTPS
- URL: your Edge Function URL
- Generate secret — copy the
v1,whsec_…value intoSEND_EMAIL_HOOK_SECRET - Save
Redeploy or update secrets if you generated the secret after the first deploy.
While the hook is enabled, Supabase does not send auth mail itself — your function must succeed and return 200 + {}.
4. Auth email types
Handle every email_action_type your app uses:
email_action_type |
Typical use |
|---|---|
signup |
Email confirmation |
recovery |
Password reset |
magiclink |
Passwordless sign-in |
invite |
User invite |
email_change |
Confirm email change |
email_change_new |
Confirm new address (secure email change) |
reauthentication |
Step-up OTP |
If the hook is on and a type is missing from your templates, that mail can fail silently.
5. Sandbox vs production
| Stage | from |
to |
|---|---|---|
| Sandbox (no domain verified) | noreply@mail.supersendtx.com (or your sandbox domain) |
Your SuperSend TX account email only |
| Production | noreply@yourdomain.com |
Any recipient |
Verify your domain in SuperSend TX before switching SUPERSENDTX_FROM_EMAIL.
6. Webhooks (optional)
Subscribe to email.delivered, email.bounced, and email.complained in SuperSend TX to monitor auth mail health. See Webhooks.
Lovable / AI builders
If you generated the app in Lovable, store SUPERSENDTX_API_KEY in project secrets and ask the agent to wire this hook — see Lovable + SuperSend TX.