Mailbeam
Supabase + Edge FunctionsIntermediate25 minutesUpdated January 2025

Email Verification with Supabase

Supabase allows you to intercept authentication events via Auth Hooks. This tutorial creates an Edge Function that calls Mailbeam before Supabase creates a new user, blocking disposable and invalid emails at the identity layer.

Prerequisites

  • Supabase project (CLI installed)
  • A Mailbeam API key (sign up free)

Create the Edge Function

supabase functions new verify-email
// supabase/functions/verify-email/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";

const MAILBEAM_KEY = Deno.env.get("MAILBEAM_KEY")!;
const MIN_SCORE = 60;

serve(async (req) => {
  const payload = await req.json();
  // Auth Hook payload has user data in payload.user
  const email = payload?.user?.email ?? payload?.email;

  if (!email) {
    return new Response(JSON.stringify({ decision: "reject" }), {
      headers: { "Content-Type": "application/json" },
    });
  }

  try {
    const response = await fetch("https://api.mailbeam.dev/v1/verify", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${MAILBEAM_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email }),
      signal: AbortSignal.timeout(5000),
    });

    if (!response.ok) throw new Error(`HTTP ${response.status}`);

    const { valid, score, reason } = await response.json();

    if (!valid || score < MIN_SCORE) {
      const message =
        reason === "disposable_domain"
          ? "Please use a permanent email address."
          : "Please provide a valid, reachable email address.";

      return new Response(
        JSON.stringify({ decision: "reject", message }),
        { status: 200, headers: { "Content-Type": "application/json" } }
      );
    }
  } catch (err) {
    // Fail open — don't reject on Mailbeam API errors
    console.error("Mailbeam error:", err);
  }

  return new Response(
    JSON.stringify({ decision: "continue" }),
    { status: 200, headers: { "Content-Type": "application/json" } }
  );
});

Set environment variable

supabase secrets set MAILBEAM_KEY=mb_live_xxxxxxxxxxxxxxxxxxxx

Deploy and connect to Auth Hook

supabase functions deploy verify-email

In the Supabase Dashboard → Authentication → Hooks:

  • Enable Before User Creation
  • Select your verify-email Edge Function

Test locally

supabase start
supabase functions serve verify-email --env-file .env.local

# Test with curl
curl -X POST http://localhost:54321/functions/v1/verify-email \
  -H "Content-Type: application/json" \
  -d '{"user": {"email": "temp@mailinator.com"}}'
# → {"decision":"reject","message":"Please use a permanent email address."}

Best practices

  • Always return { decision: "continue" } in the catch block — fail open
  • Set AbortSignal.timeout(5000) to prevent the function from hanging
  • Log errors to Supabase Function logs for monitoring

Next steps