I IntelliAuth Docs

The auth API is the imperative side of IntelliAuth's sign-in surface — the endpoints that take credentials in, validate them, and either return a session or kick off MFA. Most apps drive these via the React SDK and never touch them directly; this topic is the reference for non-SDK consumers.

Sign in with email + password

http
POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "user@cymmetri.com",
  "password": "..."
}

Three possible response shapes:

Success without MFA:

json
{
  "data": {
    "state": "success",
    "session": { "id": "ses_01HZX...", "user_id": "usr_01HZX..." },
    "access_token": "...",
    "refresh_token": "...",
    "expires_in": 3600
  }
}

MFA required:

json
{
  "data": {
    "state": "mfa_required",
    "flow_id": "flow_01HZX...",
    "available_factors": ["webauthn", "totp", "sms"]
  }
}

Failed:

json
{
  "error": "invalid_credentials",
  "message": "Email or password is incorrect.",
  "request_id": "req_01HZX..."
}

The success-without-MFA shape includes tokens directly; this endpoint is the password-only entry point. For OAuth flows (PKCE, etc.), users do not call this — they go through /oauth2/authorize.

Sign out

http
POST /api/v1/auth/logout
Authorization: Bearer <access-token>

Returns 204 No Content on success. Revokes the current session. To revoke other sessions too, hit the per-session revoke endpoint instead (see Me API).

MFA factor list

http
GET /api/v1/auth/mfa/factors?flow_id=flow_01HZX...

When a flow returns mfa_required, fetch the factors available to this specific user:

json
{
  "data": [
    { "id": "factor_01HZX...", "kind": "webauthn", "label": "MacBook Pro" },
    { "id": "factor_01HZY...", "kind": "totp",     "label": "1Password" }
  ]
}

Show these to the user. They pick one.

Begin MFA step

For factors that require a server-side initiation (SMS sends a code, WebAuthn returns a challenge):

http
POST /api/v1/auth/mfa/begin
Content-Type: application/json

{
  "flow_id": "flow_01HZX...",
  "factor_id": "factor_01HZX..."
}

Response varies by factor kind:

json
{ "data": { "kind": "totp" } }
json
{
  "data": {
    "kind": "webauthn",
    "challenge": "<base64url-bytes>",
    "allow_credentials": [ ... ]
  }
}
json
{ "data": { "kind": "sms", "sent_to_last4": "0100" } }

Complete MFA step

http
POST /api/v1/auth/mfa/complete
Content-Type: application/json

{
  "flow_id": "flow_01HZX...",
  "factor_id": "factor_01HZX...",
  "code": "123456"
}

For WebAuthn, the body carries the assertion instead of code:

json
{
  "flow_id": "flow_01HZX...",
  "factor_id": "factor_01HZX...",
  "assertion": { "id": "...", "rawId": "...", "response": { ... }, "type": "public-key" }
}

Success response carries tokens, identical to a non-MFA sign-in.

WebAuthn primary authentication (passwordless)

http
POST /api/v1/auth/webauthn/begin-authentication
Content-Type: application/json

{}

Response:

json
{
  "data": {
    "challenge": "<base64url-bytes>",
    "rp_id": "banking-cymmetri.intelliauth.local",
    "user_verification": "required"
  }
}

The browser calls navigator.credentials.get() with the challenge, then:

http
POST /api/v1/auth/webauthn/finish-authentication
Content-Type: application/json

{
  "assertion": { ... }
}

Success returns a session and tokens.

Step-up

http
POST /api/v1/auth/step-up/initiate
Authorization: Bearer <current-access-token>
Content-Type: application/json

{
  "required_acr": "aal2"
}

Response indicates which factor to complete:

json
{
  "data": {
    "flow_id": "step_01HZX...",
    "factor": { "kind": "webauthn", "challenge": "..." }
  }
}

Complete via the MFA-complete endpoint with the returned flow_id. The response carries a new access token whose acr claim reflects the new AAL.

Common errors

Error

When

invalid_credentials

Email or password wrong

account_disabled

The user record is disabled

email_unverified

The user hasn't verified their email and tenant policy requires verification

mfa_factor_locked

Too many failed attempts; cool-down active

flow_expired

The MFA flow_id is older than the policy window

flow_invalid

The flow_id doesn't exist or doesn't belong to this caller

Every error response has request_id. Log it.

See also