Auth Endpoints
Complete reference for authentication endpoints.
Complete reference for authentication endpoints.
Public Endpoints
Require: Anon Key
Sign Up
POST /auth/signup
Authorization: Bearer <anon_key>
Content-Type: application/jsonRequest:
{
"email": "user@example.com",
"password": "password123",
"user_metadata": {
"name": "John Doe"
}
}Response: 201 Created
{
"access_token": "eyJhbGc...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "f4e3d2c1...",
"user": {
"id": "uuid",
"email": "user@example.com",
"user_metadata": {"name": "John Doe"},
"status": "active",
"created_at": "2024-01-01T00:00:00Z"
}
}Errors:
400- Invalid email/password format401- Invalid, tampered, revoked, or wrong-project anon key403- Signups disabled or anon key lacksauth.signuppermission409- Email already exists429- Rate limit exceeded
Sign In
POST /auth/signin
Authorization: Bearer <anon_key>Request:
{
"email": "user@example.com",
"password": "password123"
}Response: 200 OK (same as signup)
Errors:
400- Missing email/password401- Invalid credentials, invalid/tampered/revoked anon key, or account banned/deleted403- Anon key lacksauth.signinpermission429- Rate limit exceeded
Refresh Token
POST /auth/refresh
Authorization: Bearer <anon_key>Request:
{
"refresh_token": "f4e3d2c1..."
}Response: 200 OK
{
"access_token": "eyJhbGc...", // New token
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "f4e3d2c1...", // Same token
"user": {...}
}Errors:
401- Invalid/expired refresh token, session timeout, invalid/tampered/revoked anon key403- Anon key lacksauth.refreshpermission
Logout
POST /auth/logout
Authorization: Bearer <anon_key>Request:
{
"refresh_token": "f4e3d2c1..."
}Response: 204 No Content
Confirm Email
POST /auth/confirm
Authorization: Bearer <anon_key>
Content-Type: application/jsonRequest:
{
"token": "confirmation-token-from-email"
}Response: 200 OK
{
"message": "Email confirmed successfully"
}Also possible (200 OK):
{
"message": "Email already confirmed"
}Confirms user's email address using token from confirmation email.
Errors:
400- Missing confirmation token in request body401- Invalid/expired token or missing anon key403- CORS blocked
Resend Confirmation
POST /auth/resend-confirmation
Authorization: Bearer <anon_key>
Content-Type: application/jsonRequest:
{
"email": "user@example.com"
}Response: 200 OK
{
"message": "If the email exists and is unconfirmed, a confirmation link has been sent"
}Note: Response is generic to prevent email enumeration. If the user exists and is unconfirmed, a new token replaces the previous token (old confirmation links become invalid). If the user does not exist or is already confirmed, no email is sent.
Errors:
401- Missing/invalid anon key403- CORS blocked
Forgot Password
POST /auth/forgot-password
Authorization: Bearer <anon_key>
Content-Type: application/jsonRequest:
{
"email": "user@example.com"
}Response: 200 OK
{
"message": "If the email exists, a password reset link has been sent"
}Note: Response is always the same to prevent email enumeration.
Errors:
401- Missing/invalid anon key403- Password reset disabled or CORS blocked
Reset Password
POST /auth/reset-password
Authorization: Bearer <anon_key>
Content-Type: application/jsonRequest:
{
"token": "recovery-token-from-email",
"new_password": "NewPassword123"
}Response: 200 OK
{
"message": "Password reset successful. Please sign in with your new password."
}Effects:
- Password updated
- Recovery token cleared
- All sessions revoked
Errors:
400- Password doesn't meet requirements or in password history401- Invalid/expired token403- Password reset disabled
Sign Up Anonymous
POST /auth/signup-anonymous
Authorization: Bearer <anon_key>
Content-Type: application/jsonResponse: 201 Created
{
"access_token": "eyJhbGc...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "f4e3d2c1...",
"user": {
"id": "uuid",
"email": "anon-xyz@anonymous.volcano.internal",
"user_metadata": {"anonymous": true},
"status": "active"
}
}Creates guest user without email/password.
Errors:
401- Missing/invalid anon key403- Anonymous signins disabled or CORS blocked
User Endpoints
Require: Access Token
Get Current User
GET /auth/user
Authorization: Bearer <access_token>Response: 200 OK
{
"user": {
"id": "uuid",
"email": "user@example.com",
"email_confirmed": false,
"user_metadata": {...},
"status": "active",
"created_at": "...",
"last_sign_in_at": "..."
}
}Update User
PUT /auth/user
Authorization: Bearer <access_token>Request:
{
"password": "newpassword123",
"user_metadata": {
"name": "Jane Doe"
}
}Response: 200 OK (updated user object)
Errors:
400- Password doesn't meet requirements or in password history
Convert Anonymous User
POST /auth/user/convert-anonymous
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest:
{
"email": "user@example.com",
"password": "Password123",
"user_metadata": {
"name": "John Doe"
}
}Response: 200 OK
{
"user": {
"id": "uuid",
"email": "user@example.com",
"user_metadata": {...},
"status": "active"
}
}Converts anonymous user to authenticated user. User ID is preserved.
If require_email_confirmation=true, the converted user must confirm email
before email/password signin succeeds. When email sending is enabled, conversion
triggers a confirmation email.
Errors:
400- User is already authenticated (not anonymous)409- Email already in use
Get My Sessions
GET /auth/user/sessions
Authorization: Bearer <access_token>Response: 200 OK
{
"sessions": [
{
"id": "session-uuid",
"provider": "email",
"user_agent": "Mozilla/5.0...",
"ip_address": "192.168.1.1",
"last_ip_address": "10.0.0.50",
"is_active": true,
"is_current": true,
"created_at": "2024-01-14T08:00:00Z"
}
],
"total": 1
}Returns all sessions for the current user. The is_current field indicates which session is making the current request.
Delete My Session
DELETE /auth/user/sessions/{sessionId}
Authorization: Bearer <access_token>Response: 204 No Content
Signs out from a specific device/session.
Errors:
404- Session not found or doesn't belong to user
Delete All Other Sessions
DELETE /auth/user/sessions
Authorization: Bearer <access_token>Response: 204 No Content
Signs out from all devices except the current session. Useful for "sign out everywhere else" functionality.
Admin Endpoints
Require: Platform User Token
List Auth Users
GET /auth/users?page=1&limit=10
Authorization: Bearer <platform_token>Response: 200 OK
{
"data": [...],
"page": 1,
"limit": 10,
"total": 25,
"has_more": true
}Get Auth User
GET /auth/users/{userId}
Authorization: Bearer <platform_token>Delete Auth User
DELETE /auth/users/{userId}
Authorization: Bearer <platform_token>Response: 204 No Content
Effect: Soft delete (status='deleted'), all sessions revoked
List User Sessions
GET /auth/users/{userId}/sessions
Authorization: Bearer <platform_token>Response: 200 OK
{
"sessions": [
{
"id": "session-uuid",
"user_id": "user-uuid",
"provider": "email",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
"ip_address": "192.168.1.1",
"expires_at": "2024-01-15T12:00:00Z",
"last_activity_at": "2024-01-14T10:30:00Z",
"session_started_at": "2024-01-14T08:00:00Z",
"is_active": true,
"created_at": "2024-01-14T08:00:00Z",
"updated_at": "2024-01-14T10:30:00Z"
}
],
"total": 1
}Fields:
provider: Authentication method (email,google,github,facebook,apple,anonymous)ip_address: Client IP that created the session
Delete User Session
Revoke a specific session (log out from one device):
DELETE /auth/users/{userId}/sessions/{sessionId}
Authorization: Bearer <platform_token>Response: 204 No Content
Delete All User Sessions
Revoke all sessions (log out from everywhere):
DELETE /auth/users/{userId}/sessions
Authorization: Bearer <platform_token>Response: 204 No Content
Use cases:
- User reports compromised account
- Force password change
- Security incident response
Anon Key Endpoints
Require: Platform User Token
List Anon Keys
GET /projects/{projectId}/anon-keys
Authorization: Bearer <platform_token>Create Anon Key
POST /projects/{projectId}/anon-keys
Authorization: Bearer <platform_token>Request:
{"name": "production-key"}Response: 201 Created (includes key_value JWT)
Delete Anon Key
DELETE /projects/{projectId}/anon-keys/{keyId}
Authorization: Bearer <platform_token>Response: 204 No Content
Permanently deletes the anon key. Any clients using this key will immediately lose access.
Regenerate Anon Key
POST /projects/{projectId}/anon-keys/{keyId}/regenerate
Authorization: Bearer <platform_token>Response: 200 OK (new key_value)
Configuration Endpoints
Get Config
GET /projects/{projectId}/auth/config
Authorization: Bearer <platform_token>Update Config
PUT /projects/{projectId}/auth/config
Authorization: Bearer <platform_token>Request: (all fields optional)
{
"access_token_lifetime": 7200,
"min_password_length": 20,
"require_numbers": true,
"email_enabled": true,
"require_email_confirmation": true,
"cors_enabled": true,
"cors_allowed_origins": ["https://myapp.com"],
"device_verification_url": "https://myapp.com/device"
}Validation rules:
min_password_lengthmust be between 15 and 72 Unicode characters during the bcrypt compatibility deploymentrequire_email_confirmation=truerequiresemail_enabled=trueemail_enabled=falseis rejected whilerequire_email_confirmation=truepost_auth_redirect_urlandpost_logout_redirect_urlmust be present inallowed_redirect_urlsdevice_verification_url, when set, must be a valid http/https URL. It overrides where device-code (CLI) logins send users to approve; theuser_codeis appended automatically. Unlike the redirect URLs it is not tied toallowed_redirect_urls. Send an empty string to clear it and fall back to the managed device page. When a custom URL is set, device login does not requiremanaged_auth_enabled(but the custom page's origin must be incors_allowed_originsto call/auth/device/verify).
Send Test Email
Sends a diagnostic email using the project's saved SMTP config so you can
verify your setup works. Save your config first — the endpoint reads the
persisted auth_config, not request-time settings.
POST /projects/{projectId}/auth/config/test-email
Authorization: Bearer <platform_token>Request:
{
"to_email": "you@yourdomain.com"
}Supplying html_body and/or text_body (with an optional subject) instead
renders a custom message — used by the template editor's "Send Test" preview.
Sending subject alone (no body) is rejected.
Responses:
200—{"success": true}on delivery400— invalidto_email, oremail_enabled=false/smtp_hostempty, orsubjectwithout a body403— caller is not the project owner404— project not found502— template render failure or SMTP delivery failed (message includes the SMTP-level reason)
Managed Hosted Auth Pages
Admin endpoints (platform token required):
GET /projects/{projectId}/auth/hosted-pages/{pageType}
PUT /projects/{projectId}/auth/hosted-pages/{pageType}Supported pageType values:
loginreset-password
Public render endpoint (no auth token):
GET /projects/{projectId}/auth/hosted
GET /projects/{projectId}/auth/hosted/reset-passwordOptional unified deep links:
GET /projects/{projectId}/auth/hosted?action=login
GET /projects/{projectId}/auth/hosted?action=signup
GET /projects/{projectId}/auth/hosted?action=forgot-password
GET /projects/{projectId}/auth/hosted?action=device&user_code=ABCD-EFGH&anon_key=<anon_key>Smart login helpers (public):
GET /projects/{projectId}/auth/hosted/login/options?anon_key=<anon_key>
POST /projects/{projectId}/auth/hosted/login/check-email
Authorization: Bearer <anon_key>Rate limiting:
- Both helper endpoints are rate limited per project and client IP.
- On abuse, they apply exponential backoff and then return
429withRetry-After.
Behavior notes:
- Render requires
Accept: text/html. - Latest saved page content is rendered.
- Managed pages render only when
managed_auth_enabled=true. GET /projects/{projectId}/auth/hostedis the unified login/signup/forgot-password entrypoint.GET /projects/{projectId}/auth/hosted?action=device&user_code=...&anon_key=...renders managed device authorization approval UX.GET /projects/{projectId}/auth/hosted/reset-passwordis the dedicated reset-password page.- Redirect users from your app to
GET /projects/{projectId}/auth/hosted?anon_key=<anon_key>to use managed hosted auth. - HTML validation rejects
<script>,javascript:URLs, inline event handlers, andiframe/object/embed/meta/linktags. - CSS validation rejects closing
</styleand</headtag patterns. - HTML and CSS are each limited to 256 KiB.
- Public render responses include strict CSP,
X-Frame-Options: DENY,X-Content-Type-Options: nosniff, andCache-Control: no-store.
See Also
- Authentication - Auth headers and token types
- Errors - Error handling