POST
Log in
Send the user’s email and password. The response is always a challenge, never tokens — even with the correct password you get the next step to complete. Read the challenge field and follow the matching row in the table below; completing that step is what gives you tokens.
Prefer passkey sign-in where you can — it’s our recommendation. This password flow is the fallback.
A login reply looks like this:
authenticated is always false here — login hands you a challenge to complete, never tokens directly. You get tokens from the step that the challenge points you to.

What each challenge means

The email-verification and temporary-password paths don’t end at those steps — both finish with a SECURITY_SETUP_REQUIRED challenge, where the user sets up a passkey or 2FA before any tokens are issued. See Complete security setup.
Some challenges carry a session_token and some don’t. The sessionless one (NEW_PASSWORD_REQUIRED) just sends the user to a flow that starts fresh with their email — you don’t pass a token along.
Too many wrong passwords locks the account, and you’ll get a 423 with code USER_LOCKED.

Headers

x-program-id
string
required

Identifies the program. Can also be sent as the programId query parameter.

Body

application/json
email
string<email>
required
password
string
required

Response

The next challenge to complete (login never returns tokens directly)

success
boolean
Example:

true

data
object

Login never returns tokens directly — it always returns the next challenge to complete. Read challenge and follow the matching step; complete it to receive tokens.