Supabase · TypeScript (Deno)

Block disposable email in Supabase

Use a Supabase Edge Function as an Auth Hook. Calls Piisend before Supabase creates the user — rejects signup at the auth layer.

The code

// supabase/functions/before-user-created/index.ts
import { Webhook } from "https://esm.sh/standardwebhooks@1.0.0";

const PIISEND_URL = "https://api.piisend.com/api/v1/intelligence/email";

async function isDisposable(email: string): Promise<boolean> {
  try {
    const r = await fetch(PIISEND_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${Deno.env.get("PIISEND_API_KEY")}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email }),
    });
    if (!r.ok) return false;
    const data = await r.json();
    return data.signals?.disposable === true;
  } catch {
    return false;
  }
}

Deno.serve(async (req) => {
  const payload = await req.text();
  const headers = Object.fromEntries(req.headers);
  const wh = new Webhook(Deno.env.get("SUPABASE_AUTH_HOOK_SECRET")!);
  const { user } = wh.verify(payload, headers) as { user: { email?: string } };

  if (user.email && await isDisposable(user.email)) {
    return new Response(
      JSON.stringify({ error: { message: "Please use a permanent email address.", http_code: 400 } }),
      { status: 400 },
    );
  }

  return new Response(JSON.stringify({}), { status: 200 });
});

API key

Set `PIISEND_API_KEY` as a Supabase Edge Function secret via `supabase secrets set`.

Fail-open vs fail-closed

Returns allow on API errors — tune for your fraud tolerance.

Pair with verification

Register the function as a "Before user created" auth hook in the Supabase dashboard.

Send verification email with Piisend

After blocking disposable addresses, send OTP and verification mail through the same Piisend account. One API key for intelligence checks and transactional sends.