Enroll in MFA

POST /v1/mfa/enroll

Start turning on two-factor authentication for your user. The call generates a new TOTP secret and ten one-time backup codes and returns them; MFA is not on yet. Add the secret to an authenticator app (scan qr_code or type in secret), then confirm with a current code at /mfa/activate. Until then sign-in is unchanged and /mfa/status reads enabled: false.

Interactive session only. This call requires an interactive dashboard session (a session JWT): sign in with /auth/login (followed by /auth/mfa when MFA is on) and send the token as Authorization: Bearer <session_jwt>. API keys and OAuth/MCP tokens get 403 with code: "interactive_session_required".

The secret and the backup codes are shown only in this response; the server keeps the secret encrypted and the codes hashed. Store the backup codes somewhere safe: each one signs you in once if you lose the authenticator.

Enrolling again before activating replaces the pending enrollment (new secret, new codes — the earlier ones stop working). If MFA is already on, the call is refused with 409: disable it first, which needs your password and a current code.

The codes are standard TOTP (RFC 6238): six digits, a 30-second step, SHA-1 — any authenticator app works.

Request body

None. Send the Content-Type: application/json header anyway — every POST without it is refused with 415.

Response (200 OK)

success boolean
Always true on a 200.
secret string
The TOTP secret, base32 (32 characters), for authenticators where you type the key in.
qr_code string
A 200×200 PNG of the QR code that carries the same secret, base64-encoded (no data: prefix — add data:image/png;base64, to show it in an <img>).
backup_codes string[]
Ten one-time backup codes, formatted XXXX-XXXX-XXXX. Each is accepted once in place of an authenticator code — at sign-in (/auth/mfa), /mfa/verify, /mfa/disable and regenerate (not at /mfa/activate). Check how many are left with /mfa/backup-codes.
created_at datetime
When the secret was generated.

Errors

401 Unauthorized
Missing or invalid auth.
403 Forbidden
The credential is an API key or an OAuth/MCP token: this call needs an interactive dashboard session (a session JWT). Body {"error": "…", "code": "interactive_session_required"}.
409 Conflict
MFA is already on (plain-text body): MFA is already enabled for this account. Disable it first (requires your password and a current code) before re-enrolling.
415 Unsupported Media Type
The Content-Type: application/json header is missing.
500 Internal Server Error
The secret, the codes or the record could not be created (plain-text body, e.g. Failed to save MFA record).
Request
curl -X POST "https://api.ngris.com/v1/mfa/enroll" \ -H "Authorization: Bearer <session_jwt>" \ -H "Content-Type: application/json"
Response — 200 OK
{ "success": true, "secret": "EXAMPLEEXAMPLEEXAMPLEEXAMPLE2345", "qr_code": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADI...", "backup_codes": [ "SAMP-LE22-AAAA", "SAMP-LE22-BBBB", "SAMP-LE22-CCCC", "SAMP-LE22-DDDD", "SAMP-LE22-EEEE", "SAMP-LE22-FFFF", "SAMP-LE22-GGGG", "SAMP-LE22-HHHH", "SAMP-LE22-IIII", "SAMP-LE22-JJJJ" ], "created_at": "2026-10-20T09:14:03.512338771Z" }
On this page
Enroll in MFA