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
emailRedirectTowhen you want numeric OTP delivery.Re-test the flow through the gateway's
supabase_passwordless_authandsupabase_passwordless_verifyendpoints.
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:
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:
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
Magic Link vs OTP
A common point of confusion is the difference between magic links and OTP codes in Supabase:
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
signInWithOtp CreatesSupabase 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:
The client calls the gateway's Supabase auth endpoint with an email or phone number.
The gateway calls Supabase to request the OTP.
The user submits the OTP to the gateway's verify endpoint.
Supabase verifies the OTP and returns tokens.
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.
Issue: Receiving Magic Links Instead of OTP Codes
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:
Call the auth endpoint to send a code.
Receive a numeric OTP by email or SMS.
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
Go to https://supabase.com/dashboard
Select your project.
Navigate to Authentication then Settings
Step 2: Configure Email Auth Settings
In the Auth Settings section:
Find "Email OTP" Settings:
Look for "Email OTP" configuration
Enable "Email OTP" if it's disabled
Disable Magic Links (if needed):
Look for "Magic Link" settings
Consider disabling magic links to force OTP usage
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
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:
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
Go to Authentication then Email Templates in the Supabase Dashboard
Select "Magic Link" or find "OTP" template
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
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
Go to Authentication then Settings in the Supabase Dashboard
Find the "Phone Auth" section
Enable phone authentication
Configure your SMS provider (Twilio, MessageBird, Vonage, etc.)
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
Still Receiving Magic Links Instead of OTP Codes
Check these in order:
Email template: Verify the template uses
{{ .Token }}and not{{ .ConfirmationURL }}.Project settings: Ensure Email OTP is enabled in Supabase Auth settings.
emailRedirectTo: Remove it fromsignInWithOtp. If it is set, Supabase can switch to a link-based flow.Gateway flow: Make sure the client is using
/api/v1/supabase/authand/api/v1/supabase/verify, not mixing direct Supabase calls with gateway endpoints.Retest with a fresh email: This helps rule out template caching or rate-limit noise.
OTP Code Not Arriving
Check these in order:
Look in spam or junk folders.
Verify the email address or phone number is correct.
Check Supabase Dashboard logs for delivery errors.
Confirm your project has not exceeded email or SMS sending limits.
For phone OTP, verify the SMS provider credentials and provider logs.
Token Verification Fails
Check these in order:
Confirm the OTP code has not expired.
Confirm the client is POSTing to
/api/v1/supabase/verify.Check that the token is a numeric OTP code, not a clicked magic-link flow.
Verify
SUPABASE_JWT_SECRETandSUPABASE_SERVICE_ROLE_KEY.Verify the
issuerfield in the authorizer matches your Supabase project URL.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
123456in 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_tokenandrefresh_token.
The key is ensuring your Supabase project is configured to prioritize OTP codes over magic links in the authentication flow.
Related Pages
Authentication Guide -- full authentication setup for Serverless API Gateway
Authorizer Configuration -- JWT and provider-based authorization
CORS Configuration -- configure cross-origin requests for browser-based OTP flows
Auth0 Integration -- alternative authentication provider
Supabase Passwordless Quickstart -- step-by-step quickstart guide
Introduction -- overview of Serverless API Gateway
Last updated