Skip to main content

POST /v1/auth/email-otp/verify

Exchange a 6-digit code from /request for a browser session.

::: tip Auth Required: none. The email + code IS the credential. :::

Request

POST /v1/auth/email-otp/verify

HeaderRequiredNotes
Content-Type: application/jsonyes

Body

{
"email": "alice@example.com",
"code": "123456"
}

Response

200 OK — status: "success"

If the user does NOT have MFA enabled: a full LoginResponse::Success body. Cookies set: identsphere_at, identsphere_rt, identsphere_csrf.

200 OK — status: "mfa_required"

If the user has MFA enabled:

{
"status": "mfa_required",
"mfa_token": "eyJ...",
"mfa_token_expires_in": 300
}

The caller must POST to /v1/auth/mfa/challenge with the mfa_token plus a TOTP/recovery code.

Error responses

StatusCodeWhen
400invalid_inputEmail or code missing / malformed.
401authentication_requiredUnknown email, expired code, wrong code, or attempts exhausted. (Same response across all failure modes.)
500internal_errorDB or JWT failure.

Example: curl

curl -X POST https://auth.example.com/v1/auth/email-otp/verify \
-H 'Content-Type: application/json' \
-c cookies.txt \
-d '{"email":"alice@example.com","code":"123456"}'

Notes

  • Wrong codes increment the attempts counter on the latest live challenge. After 5 misses the challenge is locked even if the right code is submitted later — the user must request a new one.
  • Disabled / deleted accounts can't sign in even with the right code.
  • The session is minted at AAL1 unless MFA is enrolled (in which case the caller must complete the challenge for AAL2).