threshold1

Backup Codes

Generation needs a session; verification deliberately doesn't — see Backup Codes for why (it's a real account-recovery login path). Real values (count, format, hashing) are covered there too.

Generate

POST /api/v1/backup-codes/generate
Authorization: Bearer <threshold1_jwt>
Content-Type: application/json

{}

Real response, captured live — 10 real codes, shown exactly once:

{
  "success": true,
  "codes": [
    "GZ8H2-Y2HPP", "T7C5R-8F6VA", "JPEK8-9P4EW", "ER7H5-KXQF7", "ZC9J6-4CTAM",
    "Q28XP-SQ97R", "8G7WS-A7QF9", "BHWAW-S9KDX", "J7EEZ-CA55S", "8VCQ9-FW6RP"
  ]
}

Calling this again immediately invalidates this entire batch and returns a new one — there's no way to retrieve a previously-generated set again.

Verify (recover)

POST /api/v1/backup-codes/verify
Authorization: Bearer th_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{ "email": "user@example.com", "code": "GZ8H2-Y2HPP" }

Authenticated by API key, not a user JWT — the whole point is recovering access when the user has no session. { "externalUserId": "...", "code": "..." } works the same way if you're using the identity bridge instead of email.

Real response, captured live with a real generated code:

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "userId": "274f4b7d-34cb-4770-941f-71f60681bd8b"
}

A used code, or one from an invalidated batch, returns invalid_code. See Error Reference for the full list.

What's next

  • Backup Codes (Methods Reference) — count, format, bcrypt hashing, the 15-minute verify rate limit.
  • TOTP — the primary factor this recovers from.