Skip to content
Email API for Supabase Edge Functions

Send email from Supabase Edge Functions with an API

Send email from Supabase Edge Functions with one fetch: set the key as a secret, read it with Deno.env, and take an id back per message.

Supabase — supabase/functions/send-order-email/index.ts
// supabase/functions/send-order-email/index.ts// supabase secrets set RASKET_API_KEY=…Deno.serve(async (request: Request) => {  const apiKey = Deno.env.get("RASKET_API_KEY");  if (apiKey === undefined) {    return new Response("not configured", {      status: 500,    });  }
  const { orderId } = (await request.json()) as {    orderId: string;  };
  const response = await fetch(    "https://api.rasket.com/emails",    {      method: "POST",      headers: {        Authorization: `Bearer ${apiKey}`,        // Required on every request.        "User-Agent": "acme-billing/1.0",        "Content-Type": "application/json",        "Idempotency-Key": `order-${orderId}`,      },      body: JSON.stringify({        from: "Acme <orders@send.acme.example>",        to: ["ronald.williams@example.com"],        subject: "Your order has shipped",        html: "<p>Order 1042 shipped today.</p>",      }),    },  );
  if (!response.ok) {    return new Response("send failed", { status: 502 });  }
  const email = (await response.json()) as {    id: string;  };  return Response.json({ id: email.id });});

What sending from Supabase Edge Functions actually involves

To send email from Supabase Edge Functions you write a Deno handler that makes one HTTPS request. The function is the right place for it: the key stays a project secret instead of reaching the browser, and a database trigger or a row-level webhook can call the function when something happens that people should hear about.

Last updated 2026-09-16. Every field, header and limit below is the one the emails reference carries.

What you get with the key

Five things that arrive with the first request rather than with a later plan.

  • Idempotent sends

    Put an Idempotency-Key on a send and repeat it as often as you like. For 24 hours the same key and payload return the first response instead of a second email, and a different payload under that key is refused rather than sent.

  • Signed webhooks you can replay

    Delivery, bounce, complaint, open and click posted to your endpoint with a timestamp and a signature, retried on failure and replayable from the dashboard.

  • Inbound mail on your own domain

    Receive at your domain on every plan. Each message arrives on a signed webhook with its headers, text and HTML, and its attachments behind signed links.

  • Templates with typed variables

    Publish a template, then send it by ID or alias with the values it declares. Every email records the exact version it was rendered from.

  • Broadcasts and automations

    Contacts with typed properties, topics people subscribe to, and per-contact workflows that wait, branch and send — through the same pipeline as the rest of your mail.

How to send email from Supabase Edge Functions

Six steps, in this order. Everything before the fifth is done once, and only the fifth is about the language you write in.

  1. 1

    Create an API key

    Open API keys in the dashboard and create one. It is shown once and stored hashed, so copy it then. Put it in your environment as RASKET_API_KEY and read it from there; a key committed to a repository is a key you have to rotate.

  2. 2

    Add a sending domain

    POST /domains with a domain you control. A subdomain such as send.acme.example is the usual choice, because it keeps this mail's reputation separate from the address your people write from. The response carries a records array.

  3. 3

    Publish the DNS records

    Every row of records goes into your DNS: the DKIM TXT record at rasket._domainkey, and the two records that give the domain its own return path. Nothing sends from the domain until they resolve.

  4. 4

    Verify the domain

    POST /domains/{domain_id}/verify. The domain comes back with a status on each record, and once it reads verified you can send from any address on it. An unverified sender is refused rather than quietly dropped.

  5. 5

    Send your first email from Supabase Edge Functions

    Post the message with the sample above. Carry an Idempotency-Key — yours or your queue's retry cannot then turn into a second email — and a User-Agent, which every request has to have. A 200 with an id means we have taken responsibility for the message.

  6. 6

    Subscribe a webhook and check its signature

    POST /webhooks with an https endpoint and the events you care about. Delivery, bounce, complaint, open and click arrive there with svix-id, svix-timestamp and svix-signature; verify against the raw body before anything parses it.

Verify webhooks in Supabase Edge Functions

An edge function can receive Rasket's webhooks too. Deploy it with --no-verify-jwt so the request reaches your code — we send a signature, not a Supabase token — read await request.text() first, and check svix-id, svix-timestamp and svix-signature against an HMAC of id.timestamp.body before anything parses the JSON.

The five steps a verifier performs, in seven languages, are on the webhooks reference.

Questions about Supabase Edge Functions

Do I need an SMTP server to send email from Supabase?

No. Supabase's own SMTP setting is for its auth emails; product mail you send yourself is an HTTPS call from an edge function, with no relay to configure and no credentials beyond one project secret.

Where do I keep the API key in an edge function?

As a project secret: supabase secrets set RASKET_API_KEY=… and read it with Deno.env.get inside the handler. Never put it in a table, in the client, or in a NEXT_PUBLIC-style variable — anything the browser can reach is public.

Can a database trigger send the email?

Yes, indirectly, and that is the usual shape: a trigger or a Database Webhook calls the edge function, and the function calls us. Pass the row's id through and use it as the idempotency key, so a redelivered trigger cannot send the mail twice.

How do I stop a retry from sending the same email twice?

Send an Idempotency-Key header built from the row the mail is about rather than a fresh value per invocation. For 24 hours the same key with the same payload returns the first response instead of sending again, and a different payload under that key is refused.

How do I verify a Rasket webhook in an edge function?

Deploy the receiving function with --no-verify-jwt, read await request.text() as its first statement, and verify the three svix headers against an HMAC of id.timestamp.body with your whsec_ secret using crypto.subtle. Answer 400 when it does not match, and 200 once you have stored the event.

Make your first send

Create a key, verify a domain, and post your first message from Supabase Edge Functions. The free plan does not expire.