For the complete documentation index, see llms.txt. This page is also available as Markdown.

Supabase OTP

Supabase OTP for Serverless API Gateway. Fix magic link vs OTP issues, configure signInWithOtp, email templates, phone OTP, and verify flows on Cloudflare Workers.

This guide explains how to configure Supabase OTP (one-time password) authentication with the Serverless API Gateway. It covers email OTP, phone OTP, signInWithOtp, magic link vs OTP behavior, environment variables, email templates, and troubleshooting.

If Supabase is sending magic links instead of 6-digit OTP codes, the short answer is:

  • Make sure your Supabase email template renders {{ .Token }} instead of {{ .ConfirmationURL }}.

  • Do not pass emailRedirectTo when you want numeric OTP delivery.

  • Re-test the flow through the gateway's supabase_passwordless_auth and supabase_passwordless_verify endpoints.

If you are new to Serverless API Gateway, see the Introduction and the Supabase Passwordless Quickstart for a step-by-step walkthrough.

Supabase signInWithOtp Overview

Supabase provides the signInWithOtp method for passwordless authentication. It supports two channels:

  • Email OTP -- sends a 6-digit numeric code to the user's email address.

  • Phone OTP -- sends a 6-digit code via SMS using a configured SMS provider (Twilio, MessageBird, Vonage, etc.).

When properly configured, the Serverless API Gateway exposes two endpoints that wrap signInWithOtp and its verification counterpart:

Endpoint
Integration Type
Purpose

POST /api/v1/supabase/auth

supabase_passwordless_auth

Send OTP to email or phone

POST /api/v1/supabase/verify

supabase_passwordless_verify

Verify the OTP and return JWT tokens

For authorizer configuration details, see Authorizer. For CORS settings when calling these endpoints from a browser, see CORS.

Quick Fix Checklist

Use this checklist before reading the full guide:

Check
Correct value for OTP code flow
Why it matters

Email template variable

{{ .Token }}

Sends a numeric OTP code instead of a link

Redirect option in signInWithOtp

Omit emailRedirectTo

Redirect URLs usually push the flow toward magic links

Gateway send endpoint

POST /api/v1/supabase/auth

Triggers the OTP send flow through the Worker

Gateway verify endpoint

POST /api/v1/supabase/verify

Expects a numeric token, not a clicked link

Supabase project settings

Email OTP enabled

Required for email code delivery

A common point of confusion is the difference between magic links and OTP codes in Supabase:

Behavior
Magic Link
OTP Code

Delivery

Email only

Email or SMS

User action

Click a link in the email

Enter a 6-digit code in your app

Template variable

{{ .ConfirmationURL }}

{{ .Token }}

Requires redirect URL

Yes

No

Use case

Web apps that can handle redirects

Mobile apps, SPAs, or any app where you want an in-app verification flow

Works with gateway verify endpoint

Not directly

Yes

By default, Supabase may send a magic link even when you call signInWithOtp. The sections below explain how to force OTP code delivery instead.

What signInWithOtp Creates

Supabase signInWithOtp starts a passwordless sign-in flow. For email, the user may receive either a magic link or a numeric OTP depending on the project settings, redirect options, and email template. For phone, the user receives an SMS OTP when SMS auth is configured.

In a typical Serverless API Gateway flow:

  1. The client calls the gateway's Supabase auth endpoint with an email or phone number.

  2. The gateway calls Supabase to request the OTP.

  3. The user submits the OTP to the gateway's verify endpoint.

  4. Supabase verifies the OTP and returns tokens.

  5. Protected gateway routes validate the resulting Supabase JWT.

The important distinction is that the gateway does not replace Supabase Auth. It brokers the send and verify calls, then validates the resulting JWT on protected routes.

When you request an email OTP, Supabase sends a magic link instead of a 6-digit OTP code. This happens because the default configuration prioritizes magic links over OTP codes.

Why This Happens in Gateway Flows

Serverless API Gateway expects the client to:

  1. Call the auth endpoint to send a code.

  2. Receive a numeric OTP by email or SMS.

  3. POST that token to the verify endpoint.

If Supabase sends a magic link instead, the user never receives the numeric code that supabase_passwordless_verify expects. The result usually looks like a broken verify step or an invalid token response.

Solution: Configure Supabase Project Settings

Step 1: Access Supabase Dashboard

  1. Go to https://supabase.com/dashboard

  2. Select your project.

  3. Navigate to Authentication then Settings

Step 2: Configure Email Auth Settings

In the Auth Settings section:

  1. Find "Email OTP" Settings:

    • Look for "Email OTP" configuration

    • Enable "Email OTP" if it's disabled

  2. Disable Magic Links (if needed):

    • Look for "Magic Link" settings

    • Consider disabling magic links to force OTP usage

  3. Email Template Settings:

    • Go to Authentication then Email Templates

    • Select "Magic Link" template

    • Change the template type or configure it for OTP

Step 3: Use Explicit OTP Configuration in signInWithOtp

When emailRedirectTo is omitted or set to undefined, Supabase treats the request as an OTP request rather than a magic link request. This is the most reliable way to ensure OTP code delivery.

shouldCreateUser controls whether Supabase should create a new user when the email or phone does not already exist. Use true for sign-up or mixed sign-in/sign-up flows. Use false when only existing users should be allowed to request an OTP.

signInWithOtp Options That Matter

Option
Recommended value for OTP code flow
Effect

email

User email address

Sends a code by email

phone

User phone number

Sends a code by SMS

emailRedirectTo

Omit it

Helps avoid magic-link behavior

shouldCreateUser

true or false, depending on your auth policy

Controls whether unknown users can start the flow

Use emailRedirectTo only when you explicitly want a magic link flow. For numeric OTP verification behind the gateway, leave it out.

Environment Variables

The Serverless API Gateway requires the following environment variables for Supabase OTP integration:

Variable
Storage Method
Description

SUPABASE_URL

wrangler.toml env var

Your Supabase project URL, e.g. https://YOUR_PROJECT_ID.supabase.co

SUPABASE_JWT_SECRET

wrangler secret put

JWT secret from your Supabase project settings (used by the authorizer to validate tokens)

SUPABASE_SERVICE_ROLE_KEY

wrangler secret put

Service role key from your Supabase project settings (used server-side to call Supabase Auth API)

If OTP sending fails at the gateway layer, verify these values first before debugging templates or client code.

Set environment variables in wrangler.toml:

Set secrets using Wrangler:

API Gateway Configuration

Add the Supabase authorizer and passwordless auth paths to your api-config.json:

For a full configuration example including CORS, see the Authentication Guide.

Email OTP Template Configuration

Configure Email OTP Template

  1. Go to Authentication then Email Templates in the Supabase Dashboard

  2. Select "Magic Link" or find "OTP" template

  3. Ensure the template contains {{ .Token }} instead of {{ .ConfirmationURL }}

Example OTP email template:

If your template uses {{ .ConfirmationURL }}, the user will receive a clickable magic link. If it uses {{ .Token }}, the user will receive a 6-digit numeric OTP code.

Flow Summary

Step
Client action
Gateway behavior
Supabase behavior

1

POST email or phone to /api/v1/supabase/auth

Calls supabase_passwordless_auth

Sends OTP or magic link

2

User reads email or SMS

No gateway step

Delivers the code or link

3

POST email/phone + token to /api/v1/supabase/verify

Calls supabase_passwordless_verify

Verifies token and returns session

4

Client uses access_token

Gateway validates JWT on protected routes

Issues tokens

Supabase Email OTP: Sending and Verifying

Send Email OTP

A successful response indicates the OTP was sent. The user should receive a 6-digit code in their inbox.

If the user receives a link instead of a code, check the email template first. The template must render {{ .Token }} for an OTP-code experience.

Verify Email OTP

On success, the response includes an access_token (JWT) and a refresh_token. Use the access token in the Authorization: Bearer <token> header when calling protected endpoints.

Supabase Phone OTP

Phone OTP sends a 6-digit code via SMS instead of email. This is useful for mobile apps or when email deliverability is a concern.

Prerequisites for Phone OTP

  1. Go to Authentication then Settings in the Supabase Dashboard

  2. Find the "Phone Auth" section

  3. Enable phone authentication

  4. Configure your SMS provider (Twilio, MessageBird, Vonage, etc.)

  5. Enter the required credentials for your SMS provider

Send Phone OTP

Verify Phone OTP

The verification response is identical in structure to email OTP verification: you receive a JWT access token and refresh token.

Troubleshooting

Check these in order:

  1. Email template: Verify the template uses {{ .Token }} and not {{ .ConfirmationURL }}.

  2. Project settings: Ensure Email OTP is enabled in Supabase Auth settings.

  3. emailRedirectTo: Remove it from signInWithOtp. If it is set, Supabase can switch to a link-based flow.

  4. Gateway flow: Make sure the client is using /api/v1/supabase/auth and /api/v1/supabase/verify, not mixing direct Supabase calls with gateway endpoints.

  5. Retest with a fresh email: This helps rule out template caching or rate-limit noise.

OTP Code Not Arriving

Check these in order:

  1. Look in spam or junk folders.

  2. Verify the email address or phone number is correct.

  3. Check Supabase Dashboard logs for delivery errors.

  4. Confirm your project has not exceeded email or SMS sending limits.

  5. For phone OTP, verify the SMS provider credentials and provider logs.

Token Verification Fails

Check these in order:

  1. Confirm the OTP code has not expired.

  2. Confirm the client is POSTing to /api/v1/supabase/verify.

  3. Check that the token is a numeric OTP code, not a clicked magic-link flow.

  4. Verify SUPABASE_JWT_SECRET and SUPABASE_SERVICE_ROLE_KEY.

  5. Verify the issuer field in the authorizer matches your Supabase project URL.

  6. Re-test after sending a brand new code so you are not verifying an old token.

Code-Level Fix (If Dashboard Configuration Does Not Work)

If the dashboard configuration does not resolve the issue, try using the admin client with explicit OTP type:

Expected Behavior After Configuration

After proper configuration:

  • Email OTP: You receive a 6-digit code like 123456 in your email.

  • Phone OTP: You receive a 6-digit code via SMS.

  • Send response: The auth endpoint confirms that Supabase accepted the OTP request.

  • Verification response: The verify endpoint returns access_token and refresh_token.

The key is ensuring your Supabase project is configured to prioritize OTP codes over magic links in the authentication flow.

Last updated