https://console.vast.ai.
Authentication
The 2FA endpoints accept different authentication shapes depending on whether the caller is the UI (logged-out or logged-in) or the CLI. Several endpoints can be called without a Bearer token — they accept atfa_secret from a prior login or send-code response instead.
UI flow (logged out — login path)
When a user is logged out and entering credentials in the UI, the regular login endpoint returns atfa_secret. The client then calls 2FA endpoints (tfa/, tfa/sms/, tfa/email/, tfa/sms/resend/) using only that tfa_secret — no Bearer token is required. The login endpoint completes the 2FA challenge and returns the user object; for this UI flow, the session is established via cookie/header (the remember() mechanism on the backend) — session_key is not returned in the response body. See the per-endpoint Login section below for the CLI-vs-UI distinction on session_key placement.
UI flow (logged in — adding/removing methods)
When the user is already logged in via the UI, calls to 2FA endpoints carry a Bearer token (Authorization: Bearer <api_key> header set by the frontend client) — and the browser’s session cookie is sent in parallel as a separate auth mechanism. Calls may also carry a tfa_secret from a recent send-code response. For some endpoints (e.g., authorize-new-method, remove method), both are accepted; the docs per endpoint specify the exact requirement.
CLI flow
CLI callers authenticate with their normal API Key as a Bearer token. The 2FA endpoints called from the CLI typically don’t need atfa_secret because the API key already authenticates the user — except for paths where a code-flow is invoked, in which case the secret returned by the send-code endpoint is passed back to verify the code.
Per-endpoint summary
Footnote onbearer + secretcells:
- For
DELETE /api/v0/tfa/andPUT /api/v0/tfa/regen-backup-codes/,secretis only required when the verification method is SMS or email (challenge-based). For TOTP andbackup_codeverification, thesecretfield is not needed — the TOTP secret lives in the DB and backup codes are self-validating.- For
POST /api/v0/tfa/confirm-new/,secretis always required — but its meaning depends on the method being added: for SMS, it’s thetfa_secretfrom the SMS challenge; for TOTP, it’s the base32 TOTP secret returned by/tfa/totp-setup/. Same JSON field, different value depending ontfa_method.
Email verification prerequisite
Email verification (email_verified_at non-null on the user record) is required before any 2FA endpoint that sends codes (SMS or email).
Because first-time 2FA setup uses the email-send flow as its authorize step, email verification is also a prerequisite for the first-method setup. Adding subsequent methods may or may not require it, depending on which existing method the user authorizes with — TOTP-only or backup-code authorization paths don’t trigger code sending and therefore don’t require email verification.
Admin users (is_admin=True) are exempt from the email-verified prerequisite on the SMS send endpoints (POST /api/v0/tfa/sms/, POST /api/v0/tfa/sms/resend/) — useful for support and testing flows. The exemption does not apply to other endpoints.
Status
Get 2FA status
success— request status flagtfa_enabled— boolean reflecting theusers.tfa_enabledDB column. This is a boolean only — it does NOT distinguishenabledfromlegacy. To get that enum, use thetfa_statusfield on the user object returned byshow user/GET /api/v0/users/current/.methods[]— list of configured 2FA methods. Each entry includes:id— method ID (use astfa_method_idwhen disambiguating multiple methods of the same type)user_id— owning usermethod—"sms"or"totp"label— user-set display labelphone_number— masked (last 4 digits shown) for SMS; TOTP secrets are never returnedis_primary— whether this is the user’s primary methodfail_count— current failed-attempt count for the methodlocked_until— Unix timestamp when this method’s lockout expires, ornullif not lockedcreated_at/last_used— timestamps
backup_codes_remaining— count of unused backup codesnew_method_authorized— whether the user has completed the authorization step to add a new method (short-lived, 30-minute window)
enabled|disabled|legacy enum — the tfa_enabled boolean above is a binary flag. To distinguish enabled from legacy, read the tfa_status field on the user object returned by show user / GET /api/v0/users/current/.
Login
Complete 2FA login
Response
Returns the authenticated user object (the same fields as the
show user endpoint), plus a session_key field on CLI-flow responses and the conditional response fields documented below.
session_key placement depends on caller flow:
- CLI flow (request authenticated via API key as Bearer): the response body includes a
session_keyfield. CLI callers automatically use this value asAuthorization: Bearer <session_key>for subsequent authenticated requests. - UI logged-out flow (request authenticated via username/password +
tfa_secret): the session is set as a cookie/header via theremember()mechanism and is NOT included in the response body. The browser automatically uses cookie auth for subsequent requests; no client-side session_key handling is needed.
backup_codes_remaining— included only when the caller authenticated with abackup_codeAND has at least one remaining unused code. The value is the count of remaining unused backup codes after this consumption. Note: the field is omitted when the count is 0 — a user consuming their last backup code will not see this field in the response. Treat its absence as either “didn’t use a backup code” or “used the last one”; checktfa_statusandmethods[]fromGET /api/v0/tfa/status/for definitive state.legacy_tfa_user: true— included only for legacy 2FA accounts (the field is omitted otherwise — there is no explicitfalsevalue sent). Signals the frontend to show a migration banner.
Remove a 2FA method
Response
{"msg": "2FA Successfully Disabled"} — tfa_enabled is set to false and all backup codes are soft-deleted.
The same"2FA Successfully Disabled"message is also returned when a legacy 2FA account callsDELETE /api/v0/tfa/. In that case, no backup codes are soft-deleted because legacy users do not have any backup codes stored.
Sending Codes
Send email verification code
secret (tfa_secret from this response) must then be passed back as the secret field when the user submits the email code, on whichever 2FA endpoint completes the verification — login (POST /api/v0/tfa/), authorize-new-method confirm (PUT /api/v0/tfa/authorize-new-method/), method removal (DELETE /api/v0/tfa/), regen backup codes (PUT /api/v0/tfa/regen-backup-codes/), or any other endpoint that accepts email as a verification method.
When called with a Bearer token and no secret in the request body, this endpoint generates a fresh tfa_secret for a new challenge and returns it — useful for CLI callers and for UI clients starting (or restarting) the email-verification flow. This is not a “recovery” of a prior secret — every bearer-authenticated call without a secret produces a new challenge. (The actual challenge-recovery behavior — looking up an existing challenge by phone number and rotating its secret — lives in POST /api/v0/tfa/sms/resend/.)
Send SMS verification code
Response
tfa_challenge_exists (HTTP 400) if a recent code was already sent; call the resend endpoint instead.
Resend SMS verification code
Response
Set up TOTP (Authenticator app)
POST /api/v0/tfa/confirm-new/ (see below). Until confirm-new is called successfully with a valid TOTP code, no method is added to the account.
Response
provisioning_uri is a string that encodes the TOTP secret in otpauth:// format. The UI and CLI each have client-side tools to convert this string into a QR code for display; the backend does not produce a QR image. After the user scans the QR or enters the secret manually into their authenticator app, call POST /api/v0/tfa/confirm-new/ with tfa_method: "totp" and the 6-digit code from the app.
Adding Methods
Adding a new 2FA method requires completing a two-step authorization flow first.Step 1 — Request authorization
tfa_method = "email" automatically.
Request body
Response (code flow)
Step 2 — Confirm authorization
GET /api/v0/tfa/status/ returns new_method_authorized: true.
Confirm and activate new method
Response
msg reads "TOTP 2FA method added successfully." instead of the SMS variant. The rest of the response shape is identical.
If this is the first 2FA method, the response also includes:
Update a method
Response
Backup Codes
Regenerate backup codes
tfa_enabled=true but no user_tfa_methods rows of any kind (active or soft-deleted), it backfills an SMS method using the phone number on file before generating codes — this is the legacy-account migration path.
Request body
Response
Error Codes
Note: the samemissing_auth_valueerror code is also returned with HTTP 401 byverify_user_via_secret_or_authwhen bothtfa_secretand Bearer token are missing — the request has no usable authentication context.
untracked_2fa_found detail
This error is returned only when a legacy TFA user calls the deprecated /api/v0/tfa/test-submit/ endpoint. Users accessing the current UI call /api/v0/tfa/confirm-new/ and are not affected. Legacy users are identified by a “migrate your 2FA” banner in Account Settings and should call PUT /api/v0/tfa/regen-backup-codes/ to complete migration before adding a new method.