Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/eggarcia98/auth-backend/llms.txt

Use this file to discover all available pages before exploring further.

OTP login is a two-step, passwordless flow. The user provides their email, receives a six-digit code from Supabase, and submits that code to get a full session.

How it works

1

Request an OTP

The client posts the user’s email to POST /api/v1/auth/login/otp. The server calls supabase.auth.signInWithOtp() which triggers Supabase to deliver a time-limited one-time password to that address.
2

User receives the email

Supabase sends the OTP code directly to the user’s inbox. The code is valid for a short window (typically 10 minutes, configurable in your Supabase project settings).
3

Verify the OTP

The client submits both the email and the OTP code to POST /api/v1/auth/verify-otp. The server calls supabase.auth.verifyOtp() with type: "email".
4

Session tokens returned

On success the response includes the authenticated user object and a full tokens payload with access and refresh tokens.
Unlike email/password login, OTP verification returns the tokens directly in the response body rather than setting cookies. Store and send them according to your frontend strategy — see Token Management for guidance.

Step 1 — Request OTP

Request

curl
curl -X POST https://your-domain.com/api/v1/auth/login/otp \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com"}'

Request body

FieldTypeRequiredDescription
emailstringYesValid email address to send the OTP to

Response

{
  "success": true,
  "data": {
    "message": "OTP sent to email"
  }
}
The API always returns success: true for a valid email address, even if the address is not registered. This prevents user enumeration.

Step 2 — Verify OTP

Request

curl
curl -X POST https://your-domain.com/api/v1/auth/verify-otp \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "token": "123456"}'

Request body

FieldTypeRequiredDescription
emailstringYesThe same email used in step 1
tokenstringYesThe OTP code from the email

Response — success

{
  "success": true,
  "data": {
    "user": {
      "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "email": "user@example.com",
      "emailVerified": true,
      "provider": "email",
      "createdAt": "2024-01-15T10:00:00.000Z",
      "updatedAt": "2024-01-15T10:00:00.000Z"
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
      "expiresIn": 3600
    }
  }
}

Error cases

ScenarioHTTP statusError message
Missing or invalid email400Invalid email address
OTP code expired401Invalid or expired OTP
OTP code incorrect401Invalid or expired OTP
Email/token mismatch401Invalid or expired OTP

Error response

{
  "success": false,
  "error": {
    "message": "Invalid or expired OTP",
    "code": "UNAUTHORIZED"
  }
}

Frontend integration

TypeScript
// Step 1: request OTP
await fetch('/api/v1/auth/login/otp', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email }),
});

// Step 2: verify code entered by the user
const res = await fetch('/api/v1/auth/verify-otp', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email, token: otpCode }),
});

const { data } = await res.json();
// data.tokens.accessToken is now available
// data.user contains the authenticated user
If you want tokens stored in cookies instead of the response body, pass the access and refresh tokens to your own cookie-setter endpoint after verification, or use the standard login flow once the user has confirmed their email.

Build docs developers (and LLMs) love