Authentication API
REST API endpoints for sign-in, sign-up, sign-out, token refresh, magic links, and OAuth.
Authentication API
Endpoints for user authentication. Most endpoints accept a publishable key (pk_live_...) in the Authorization header and require X-Tenant-ID.
POST /v1/auth/sign-up
Register a new user with email and password.
Headers:
| Header | Required | Value |
|---|---|---|
Authorization | ✅ | Bearer pk_live_... |
X-Tenant-ID | ✅ | Tenant ID |
Content-Type | ✅ | application/json |
Body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | ✅ | User's email address |
password | string | ✅ | Password (min 8 chars) |
name | string | — | Full name |
username | string | — | Unique username (if enabled) |
metadata | object | — | Custom public_metadata |
Responses:
201 Created— User created, session issued409 Conflict—email_already_exists422 Unprocessable—password_too_weak,invalid_email
curl -X POST https://api.hellojohn.dev/v1/auth/sign-up \
-H "Authorization: Bearer pk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"password": "s3cur3P@ssw0rd",
"name": "Alice Smith"
}'Response:
{
"user": {
"id": "usr_01HABCDEF123456",
"email": "alice@example.com",
"name": "Alice Smith",
"email_verified": false,
"created_at": "2024-01-15T10:30:00Z"
},
"session": {
"id": "ses_01HABCDEF999888",
"access_token": "eyJhbGciOiJFZERTQSJ9...",
"refresh_token": "rft_01HABCDEF777666",
"access_token_expires_at": "2024-01-15T11:30:00Z",
"refresh_token_expires_at": "2024-02-14T10:30:00Z"
}
}POST /v1/auth/sign-in
Sign in with email and password.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | ✅ | User's email |
password | string | ✅ | User's password |
captcha_token | string | — | Captcha token if bot protection is enabled |
Responses:
200 OK— Returns user + session (same shape as sign-up)401 Unauthorized—invalid_credentials403 Forbidden—mfa_required(see below),account_locked429 Too Many Requests—rate_limit_exceeded
curl -X POST https://api.hellojohn.dev/v1/auth/sign-in \
-H "Authorization: Bearer pk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{"email": "alice@example.com", "password": "s3cur3P@ssw0rd"}'MFA Required Response
When MFA is required, the server returns 403 with a challenge token:
{
"error": {
"code": "mfa_required",
"status": 403,
"mfa_challenge": {
"challenge_id": "chg_01HABCDEF555444",
"factor_type": "totp",
"expires_at": "2024-01-15T10:35:00Z"
}
}
}Use challenge_id with POST /v1/auth/mfa/verify to complete authentication.
POST /v1/auth/sign-out
Revoke the current session.
Headers:
| Header | Required | Value |
|---|---|---|
Authorization | ✅ | Bearer <access_token> |
curl -X POST https://api.hellojohn.dev/v1/auth/sign-out \
-H "Authorization: Bearer eyJhbGciOiJFZERTQSJ9..."Response: 204 No Content
POST /v1/auth/token/refresh
Exchange a refresh token for a new access token. Implements refresh token rotation — the old refresh token is invalidated and a new one is issued.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
refresh_token | string | ✅ | Current refresh token |
curl -X POST https://api.hellojohn.dev/v1/auth/token/refresh \
-H "Authorization: Bearer pk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{"refresh_token": "rft_01HABCDEF777666"}'Response:
{
"access_token": "eyJhbGciOiJFZERTQSJ9...",
"refresh_token": "rft_01HABCDEF888777",
"access_token_expires_at": "2024-01-15T12:30:00Z",
"refresh_token_expires_at": "2024-02-14T11:30:00Z"
}POST /v1/auth/magic-link
Send a magic link email to the user. The link expires in 15 minutes by default.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | ✅ | User's email address |
redirect_url | string | ✅ | URL to redirect after clicking the link |
create_if_not_exists | boolean | — | Register user if they don't exist (default: false) |
curl -X POST https://api.hellojohn.dev/v1/auth/magic-link \
-H "Authorization: Bearer pk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"redirect_url": "https://app.example.com/auth/callback"
}'Response: 200 OK
{ "sent": true }POST /v1/auth/magic-link/verify
Verify a magic link token (called automatically via the redirect URL).
Body:
| Field | Type | Required | Description |
|---|---|---|---|
token | string | ✅ | Token from the magic link URL |
Response: Same shape as sign-in — returns user + session.
POST /v1/auth/mfa/verify
Complete MFA verification during sign-in.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
challenge_id | string | ✅ | From the mfa_required error response |
code | string | ✅ | TOTP code, OTP, or backup code |
factor_type | string | ✅ | totp, sms, email_otp, backup_code |
curl -X POST https://api.hellojohn.dev/v1/auth/mfa/verify \
-H "Authorization: Bearer pk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"challenge_id": "chg_01HABCDEF555444",
"code": "847293",
"factor_type": "totp"
}'Response: Same shape as sign-in — returns user + session.
POST /v1/auth/password/forgot
Trigger a password reset email.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | ✅ | User's email |
redirect_url | string | ✅ | URL to redirect after clicking the link |
Response: 200 OK — Always returns success, even if email doesn't exist (prevents enumeration).
{ "sent": true }POST /v1/auth/password/reset
Set a new password using a reset token.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
token | string | ✅ | Token from reset email |
password | string | ✅ | New password |
Response: 200 OK — Returns user + session.
GET /v1/auth/me
Return the current authenticated user.
Headers: Authorization: Bearer <access_token>
curl https://api.hellojohn.dev/v1/auth/me \
-H "Authorization: Bearer eyJhbGciOiJFZERTQSJ9..."Response:
{
"id": "usr_01HABCDEF123456",
"email": "alice@example.com",
"name": "Alice Smith",
"email_verified": true,
"mfa_enabled": true,
"role": "member",
"public_metadata": {},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}OAuth Endpoints
See OAuth API for the full OAuth 2.0 flow endpoints.