Skip to content

Auth API

All auth routes are scoped by {app_id}. Each app has independent user accounts and tokens.

MethodPathRate LimitPurpose
POST/auth/{app_id}/signup5/15minRegister a new user
POST/auth/{app_id}/login10/15minSign in
POST/auth/{app_id}/magic-link5/15minSend a 6-digit sign-in code by email
POST/auth/{app_id}/magic-link/verify10/15minExchange a code for tokens
POST/auth/{app_id}/refresh20/15minRefresh access token
POST/auth/{app_id}/logoutAuth requiredEnd session
POST/auth/{app_id}/verify-email10/15minVerify email with code
POST/auth/{app_id}/forgot-password3/15minRequest reset code
POST/auth/{app_id}/reset-password5/15minReset password
GET/auth/{app_id}/meAuth requiredGet current user profile
GET/auth/{app_id}/.well-known/jwks.jsonCached 5minPublic keys for token verification
MethodPathPurpose
GET/auth/{app_id}/oauth/{provider}?redirect_to={url}Start OAuth flow
GET/auth/{app_id}/oauth/{provider}/callbackOAuth callback
POST/auth/{app_id}/oauth/{provider}/callbackPOST callback (Apple)
MethodPathPurpose
POST/v1/{app_id}/auth/oauth-configRegister a provider
GET/v1/{app_id}/auth/oauth-configList all providers
GET/v1/{app_id}/auth/oauth-config/{provider}Get provider config
PATCH/v1/{app_id}/auth/oauth-config/{provider}Update a provider
DELETE/v1/{app_id}/auth/oauth-config/{provider}Remove a provider
POST /v1/{app_id}/auth/oauth-config
{
"provider": "google",
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uris": ["https://api.example.com/auth/app_yourid/oauth/google/callback"]
}

Built-in providers (only need provider, client_id, client_secret, redirect_uris): google, github, discord, facebook, linkedin, microsoft, apple, x

Custom providers also need: authorization_url, token_url, userinfo_url

Requires provider_metadata:

{
"provider": "apple",
"client_id": "com.example.app",
"client_secret": "placeholder",
"redirect_uris": ["https://api.example.com/auth/app_yourid/oauth/apple/callback"],
"provider_metadata": {
"teamId": "ABCDE12345",
"keyId": "KEY123",
"privateKey": "-----BEGIN PRIVATE KEY-----\n..."
}
}
POST /auth/{app_id}/signup
{
"email": "user@example.com",
"password": "MyP@ssw0rd!",
"display_name": "Jane Doe"
}

Password requirements: 8+ characters, uppercase, lowercase, number, special character.

{
"access_token": "eyJhbGciOi...",
"refresh_token": "...",
"expires_in": 3600,
"token_type": "Bearer",
"user": {
"id": "uuid",
"email": "user@example.com",
"email_verified": true,
"display_name": "Jane Doe",
"avatar_url": null
}
}
POST /auth/{app_id}/magic-link
{ "email": "user@example.com" }

Response is identical whether or not the email exists (prevents enumeration):

{ "message": "If an account exists with that email, a sign-in code has been sent" }

Codes expire after 15 minutes and are single-use. New users are auto-created on first verify.

POST /auth/{app_id}/magic-link/verify
{ "email": "user@example.com", "code": "123456" }

Response shape is identical to the login response. Failure responses are HTTP 400 with one of: Invalid sign-in code, Sign-in code already used, Sign-in code expired.

MethodPathPurpose
GET/v1/{app_id}/configRead full app config
PATCH/v1/{app_id}/config/corsUpdate CORS origins
PATCH/v1/{app_id}/config/jwtUpdate token lifetimes
PATCH/v1/{app_id}/config/pausePause / resume the app (kill-switch). Body: { paused: boolean, reason?: string }. While paused, all data-plane traffic returns 503 with code: APP_PAUSED. Auth and control-plane endpoints stay reachable.
MethodPathPurpose
GET/v1/{app_id}/audit-logsQuery auth events. Filters: user_id, event_type, limit, offset