MFA API Reference¶
The MFA API allows dashboard clients to manage TOTP enrollment and recovery codes for the authenticated account.
Interactive sessions only
All MFA write endpoints require an interactive browser session (cookie JWT or short-lived Bearer JWT). Static access tokens receive 403 Forbidden. CLI and Terraform provider calls are unaffected by MFA enrollment.
Endpoints¶
| Method | Path | Description |
|---|---|---|
GET | /accounts/me | Current account — includes mfa field |
POST | /accounts/mfa/setup | Provision TOTP (step 1) or verify and enable (step 2) |
DELETE | /accounts/mfa | Disable TOTP and clear all recovery codes |
POST | /accounts/mfa/recovery-codes/regenerate | Regenerate recovery codes |
All responses use the standard response envelope:
MFA status on the current account¶
The mfa field on the AccountMe response reports enrollment state. The response also includes memberships[] (all organizations the account belongs to), current_organization_id, and project_memberships[] (per-project grants):
{
"metadata": { "request_id": "req_...", "organization_id": "a1b2c3d4-..." },
"data": {
"id": "...",
"email": "you@example.com",
"role": "member",
"current_organization_id": "a1b2c3d4-...",
"memberships": [
{
"id": "m1...",
"organization_id": "a1b2c3d4-...",
"role": "admin",
"permissions": { "projects": ["read", "create", "update", "delete"] }
}
],
"mfa": {
"enrolled": true,
"recovery_codes_remaining": 14,
"last_used_at": "2026-06-05T10:30:00Z"
}
}
}
mfa field shape:
| Field | Type | Description |
|---|---|---|
enrolled | boolean | true when TOTP is set up |
recovery_codes_remaining | integer \| null | Unused recovery codes. null when not enrolled. |
last_used_at | string (ISO 8601) \| null | When MFA was last successfully used. null if never. |
When not enrolled: mfa.enrolled is false and recovery_codes_remaining is null.
Enroll in TOTP — two-step flow¶
TOTP enrollment is a two-step process using the same endpoint.
Step 1 — Provision¶
Returns the provisioning data:
{
"metadata": { "request_id": "req_..." },
"data": {
"provisioning_uri": "otpauth://totp/b%27nerd%20cloud%3Ayou%40example.com?secret=JBSWY3DPEHPK3PQ&issuer=b%27nerd+cloud",
"qr_code_svg": "<svg ...>...</svg>",
"raw_secret": "JBSWY3DPEHPK3PQ"
}
}
| Field | Description |
|---|---|
provisioning_uri | Standard otpauth:// URI — pass to an authenticator app |
qr_code_svg | Inline SVG of the QR code — render as <img src="data:image/svg+xml;base64,..."> |
raw_secret | Base32 secret for manual entry. Hold this value to submit in step 2. |
Step 2 — Verify and enable¶
Submit the 6-digit code from the authenticator app along with the raw_secret returned by step 1:
POST /accounts/mfa/setup
Authorization: Bearer <token>
Content-Type: application/json
{
"otp_code": "123456",
"raw_secret": "JBSWY3DPEHPK3PQ"
}
On success, returns the recovery codes (shown once — store them immediately):
{
"metadata": { "request_id": "req_..." },
"data": {
"recovery_codes": [
"a3f7-29c1",
"b8e1-f3a0",
"c12b-8d4f",
"d9e2-7b5a",
"e4f3-6c1d",
"f5g4-5d2e",
"g6h5-4e3f",
"h7i6-3f4g",
"i8j7-5h5h",
"j9k8-6i6i",
"k0l9-7j7j",
"l1m0-8k8k",
"m2n1-9l9l",
"n3o2-0m0m",
"o4p3-1n1n",
"p5q4-2o2o"
]
}
}
16 recovery codes are returned. Each code is single-use.
Error responses for step 2:
| Status | code | Description |
|---|---|---|
422 | invalid_otp_code | Code incorrect or expired |
422 | missing_raw_secret | raw_secret not provided |
403 | forbidden | Static access token not permitted |
Disable MFA¶
DELETE /accounts/mfa
Authorization: Bearer <token>
Content-Type: application/json
{
"password": "your-current-password"
}
Clears the TOTP key and all recovery codes. Returns 204 No Content on success.
Error responses:
| Status | code | Description |
|---|---|---|
422 | invalid_password | Password incorrect |
422 | mfa_not_enrolled | No TOTP enrollment exists |
403 | forbidden | Static access token not permitted |
Regenerate recovery codes¶
POST /accounts/mfa/recovery-codes/regenerate
Authorization: Bearer <token>
Content-Type: application/json
{
"password": "your-current-password"
}
Deletes all existing recovery codes and generates a fresh set of 16. The response is the same shape as the step-2 enrollment response:
Error responses:
| Status | code | Description |
|---|---|---|
422 | invalid_password | Password incorrect |
422 | mfa_not_enrolled | TOTP is not enrolled |
403 | forbidden | Static access token not permitted |
Interactive API reference¶
All MFA endpoints are browsable in the Interactive Reference under the Accounts tag.