Skip to main content

Register

POST /api/auth/register
Creates a new user account and sends a 6-digit verification code to the provided email. If the email already exists but is not yet verified, the password and display name are updated.
Rate limited to 10 requests per 60 seconds.

Request body

email
string
required
A valid email address.
password
string
required
Password - minimum 8 characters.
displayName
string
Optional display name for the user.
{
  "email": "user@example.com",
  "password": "securePassword123",
  "displayName": "John"
}

Response 201

{
  "message": "Verification code sent to email",
  "email": "user@example.com"
}

Response 409

Returned when a verified user with this email already exists.
{
  "message": "User already exists"
}

Verify email

POST /api/auth/verify-email
Verifies the user’s email with the 6-digit code sent during registration. On success, the user is automatically logged in and receives tokens.
Rate limited to 5 requests per 60 seconds.

Request body

email
string
required
The email address to verify.
code
string
required
The 6-digit verification code. Must be exactly 6 characters.
{
  "email": "user@example.com",
  "code": "482916"
}

Response 200

Returns the user profile, tokens, and service access - identical to a login response.
{
  "user": {
    "id": "cuid_abc123",
    "email": "user@example.com",
    "displayName": "John",
    "avatar": null
  },
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "a1b2c3d4e5f6...",
  "services": [
    {
      "service": "DROP",
      "tier": "free",
      "isPremium": false,
      "connected": true
    }
  ],
  "banned": false,
  "disabled": false
}

Response 401

{
  "message": "Invalid or expired verification code"
}

Resend verification

POST /api/auth/resend-verification
Resends the email verification code. The previous code is invalidated and a new one is generated with a 15-minute expiry.
Rate limited to 3 requests per 60 seconds.

Request body

email
string
required
The email address to resend the verification code to.

Response 200

{
  "message": "Verification code resent successfully"
}

Response 409

{
  "message": "Email is already verified"
}

Login

POST /api/auth/login
Authenticates a user with email and password. If the user has 2FA enabled, the first call returns a requires2FA flag - call login again with the verificationCode field.
Rate limited to 30 requests per 60 seconds.

Request body

email
string
required
User’s email address.
password
string
required
User’s password.
verificationCode
string
TOTP verification code. Required on the second call when 2FA is enabled.

Flow: standard login

{
  "email": "user@example.com",
  "password": "securePassword123"
}

Response 200

{
  "user": {
    "id": "cuid_abc123",
    "email": "user@example.com",
    "displayName": "John",
    "avatar": null
  },
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "a1b2c3d4e5f6...",
  "services": [
    {
      "service": "DROP",
      "tier": "free",
      "isPremium": false,
      "connected": true
    }
  ],
  "banned": false,
  "disabled": false
}

Flow: 2FA login (two-step)

Step 1 - Login without verification code:
{
  "email": "user@example.com",
  "password": "securePassword123"
}
Response:
{
  "user": {
    "id": "cuid_abc123",
    "email": "user@example.com",
    "displayName": "John",
    "avatar": null
  },
  "requires2FA": true,
  "pendingToken": "eyJhbGciOiJIUzI1NiIs...",
  "message": "2FA verification required"
}
Step 2 - Login with TOTP code:
{
  "email": "user@example.com",
  "password": "securePassword123",
  "verificationCode": "482916"
}
Returns the standard login response with tokens.

Response 401

If the user’s email is not verified, a new verification code is sent automatically.
{
  "message": "Email not verified",
  "emailVerified": false,
  "email": "user@example.com"
}
{
  "message": "Invalid credentials"
}
{
  "message": "Invalid 2FA verification code"
}

Refresh tokens

POST /api/auth/refresh
Exchanges a valid refresh token for a new access token and a new refresh token. The old refresh token is revoked immediately (rotation).
Refresh tokens are valid for 30 days. Each refresh token can only be used once - a new one is issued with each rotation.

Request body

refreshToken
string
required
The current refresh token.

Response 200

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "new_refresh_token_here..."
}

Response 401

{
  "message": "Invalid or expired refresh token"
}

Get current user

GET /api/auth/me
Returns the authenticated user’s profile.
Requires Authorization: Bearer <token> header.

Response 200

{
  "user": {
    "id": "cuid_abc123",
    "email": "user@example.com",
    "avatar": "cuid_abc123/avatar_1717596600.png",
    "displayName": "John",
    "twoFactorEnabled": false,
    "createdAt": "2026-01-15T10:30:00.000Z",
    "updatedAt": "2026-06-05T14:30:00.000Z",
    "serviceAccess": [...],
    "oauthAccounts": [...]
  }
}

Logout

POST /api/auth/logout
Revokes the current session and all associated refresh tokens.
Requires Authorization: Bearer <token> header. The session ID is extracted from the JWT.

Response 200

{
  "success": true
}

Delete account

POST /api/auth/delete-account
Permanently deletes the user’s account and all associated data. Requires password confirmation and, if enabled, a 2FA code.
This action is irreversible. All user data, sessions, service entitlements, and audit logs are permanently deleted.

Request body

password
string
Required if the account has a password (non-OAuth-only accounts).
verificationCode
string
Required if 2FA is enabled on the account.

Response 200

{
  "success": true
}

Toggle 2FA

POST /api/auth/2fa
Enables or disables two-factor authentication. Enabling 2FA is a two-step process.

Enable 2FA (step 1) - get QR code

{
  "enable": true
}
Response:
{
  "qrCode": "data:image/png;base64,...",
  "manualEntryKey": "JBSWY3DPEHPK3PXP",
  "secret": "JBSWY3DPEHPK3PXP"
}

Enable 2FA (step 2) - confirm with TOTP code

{
  "enable": true,
  "verificationCode": "482916",
  "secret": "JBSWY3DPEHPK3PXP"
}
Response:
{
  "success": true
}

Disable 2FA

{
  "enable": false,
  "verificationCode": "482916"
}
Response:
{
  "success": true
}