Verification challenges
Email, password, passkey, authenticator and recovery-code endpoints, plus how a failed challenge is reported.
Verification challenges are the middle steps of the sign-in / registration flow. Every endpoint takes the flow token in Authorization: Bearer and returns a rotated flow token on success.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /verify/email | Send a code to an email address |
POST | /verify/email/confirm | Submit the email code |
POST | /verify/password | Verify the password |
POST | /verify/passkey | Get a passkey assertion challenge |
POST | /verify/passkey/confirm | Submit the passkey assertion |
POST | /verify/authenticator | Verify a TOTP code |
POST | /verify/recovery-code | Verify a recovery code |
POST /verify/email
| Parameter | Type | Required | Notes |
|---|---|---|---|
email | string | ✅ | Destination address, 3–255 chars; trimmed and lowercased |
{ "success": true, "flow_token": "eyJhbGciOi..." }In registration the address is stored as the pending email. In sign-in it is compared against the methods recorded in the flow token, and a mismatch returns success without sending mail.
POST /verify/email/confirm
| Parameter | Type | Required | Notes |
|---|---|---|---|
code | string | ✅ | Code from the email, 4–32 chars |
The code is matched against the pending codes stored inside the flow token; on success the address is marked verified in the flow.
Password
| Parameter | Type | Required | Notes |
|---|---|---|---|
password | string | ✅ | 1–128 chars |
During sign-in it is compared against the stored hash; during registration the bcrypt hash is staged in the flow token and persisted when the flow completes.
Passkey
POST /verify/passkey
No body. Returns PublicKeyCredentialRequestOptions with allowCredentials populated from the account's registered passkeys.
{ "options": { "challenge": "...", "allowCredentials": [] }, "flow_token": "eyJ..." }POST /verify/passkey/confirm
The body is an AuthenticationResponseJSON, passed through to the WebAuthn check against the stored challenge and credential public key.
Authenticator (TOTP)
| Parameter | Type | Required | Notes |
|---|---|---|---|
code | string | ✅ | Six-digit code |
Recovery code
| Parameter | Type | Required | Notes |
|---|---|---|---|
code | string | ✅ | Single-use code, 8–32 chars |
Codes are stored hashed; a match consumes the code and removes it from the set.
How failures are reported
| Endpoint | Failure shape |
|---|---|
| Passkey confirm | 200 with success: false, plus a new flow_token |
| Authenticator TOTP | 400 with success: false, plus a new flow_token |
| Recovery code | 400 with success: false, plus a new flow_token |
Failures still return a flow token
The client needs the new flow token to retry. If a failure were reported through a 4xx error body, the token could not come back and the user would have to restart sign-in.
Passkey confirm therefore always uses 200 to express "this assertion did not pass", never a status code.
Related
- Flow entry point: Sign-in and registration
- Exchanging the finished flow: Tokens
- Elevation reuses the same verification endpoints: Session elevation