How to Use Notify with Supabase Edge Functions

Send email from a Supabase Edge Function with Deno fetch and x-api-key.

Call Notify from a Supabase Edge Function when auth hooks, database webhooks, or your app need to send transactional email. Keep the API key in Edge Function secrets.

Prerequisites

  • API key — generated when you sign up
  • Verified domain (optional) — required for production sends from a custom from address
  • Supabase CLI and a project with Edge Functions enabled

Set the secret

supabase secrets set NOTIFY_API_KEY=your_api_key_here

Edge Function

Create supabase/functions/send-email/index.ts:

import { serve } from 'https://deno.land/std@0.224.0/http/server.ts';

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers':
    'authorization, x-client-info, apikey, content-type'
};

serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders });
  }

  try {
    const { to, subject, message, from } = await req.json();

    if (!to || !subject || !message) {
      return new Response(
        JSON.stringify({ error: 'to, subject, and message are required' }),
        { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
      );
    }

    const apiKey = Deno.env.get('NOTIFY_API_KEY');
    if (!apiKey) {
      return new Response(JSON.stringify({ error: 'NOTIFY_API_KEY not set' }), {
        status: 500,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }

    const response = await fetch('https://notify.cx/api/email/send', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': apiKey
      },
      body: JSON.stringify({
        to,
        subject,
        message,
        ...(from ? { from } : {})
      })
    });

    const data = await response.json();
    return new Response(JSON.stringify(data), {
      status: response.status,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: String(error) }), {
      status: 500,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    });
  }
});

Deploy:

supabase functions deploy send-email

Invoke from your app (or another Edge Function) with the project’s anon/service role headers as usual for Supabase Functions.


Auth hook example (password reset)

When you own the reset flow yourself, generate a token in your backend, then call the Edge Function (or Notify directly) with HTML that includes the reset link. For a full Next.js App Router walkthrough, see Send password reset emails.

await fetch(`${Deno.env.get('SUPABASE_URL')}/functions/v1/send-email`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: user.email,
    subject: 'Reset your password',
    message: `<p>Reset link: <a href="${resetUrl}">${resetUrl}</a></p>`
  })
});

Where to next?