---
title: "Confirm email enrollment and complete a forced-2FA login."
method: POST
path: "/api/v1/tokens/two-factor/enroll/email/confirm"
tags: ["Public"]
---

# Confirm email enrollment and complete a forced-2FA login.

`POST /api/v1/tokens/two-factor/enroll/email/confirm`

## Request

- Base URL: `https://go.outseta.com`
- URL: `https://go.outseta.com/api/v1/tokens/two-factor/enroll/email/confirm`
- Auth: none declared

Post the enrollment token (from `POST /api/v1/tokens`), the challenge
token (from `.../enroll/email/begin`), and the emailed code:
            
    { "enrollment_token": "eyJ...", "challenge_token": "eyJ...", "code": "123456" }
            
On success email 2FA is enabled and login completes — the response
carries the final access token plus the user's one-time recovery codes:
            
    {
      "confirmed": true,
      "recovery_codes": ["abcd-efgh-ijkl", "..."],
      "access_token": "eyJ...",
      "token_type": "Bearer",
      "expires_in": 31536000
    }
            
Show the `recovery_codes` to the user once — they are not returned
again. An incorrect code returns `400` (`invalid_grant`); an expired
challenge returns `410` (`challenge_expired`). Rate limited to 10
requests per minute (`429`).

## Query parameters

- `data` unknown, required

## Response `200`

Enrollment confirmed; the access token and recovery codes are returned.

- TwoFactorEnrollmentConfirmationPayload: Returned from the mid-login enrollment confirm endpoints (POST /api/v1/tokens/two-factor/enroll/{email|totp}/confirm) once the user has proven control of the new mechanism. Completes a forced-enrollment login: it both finalizes the JWT access token and surfaces the freshly generated recovery codes. The property names are snake_case to match the wire format of the other token endpoints (see TokenPayload).
  - `confirmed` boolean: Always true on a success response; the enrollment is now active.
  - `recovery_codes` string[], nullable: The user's recovery codes, generated as part of first-time enrollment. These are shown once and never returned again — prompt the user to store them somewhere safe. Each code is single-use at POST /api/v1/tokens/two-factor/recovery.
  - `access_token` string, nullable: The JWT access token. Use it as Authorization: bearer {access_token} for subsequent API calls — login is complete.
  - `token_type` string, nullable: Always Bearer.
  - `expires_in` integer: Seconds until the access token expires.

## Other responses

- `400`: The code was incorrect or missing (`invalid_grant`).
- `401`: The enrollment token is missing, invalid or expired.
- `410`: The challenge expired (`challenge_expired`); restart the login.

---

[API](https://skmtc.dev/outseta/apis/outseta-api.md) · [All operations](https://skmtc.dev/outseta/apis/outseta-api/llms.txt) · [OpenAPI document](https://skmtc.dev/outseta/apis/outseta-api/revisions/c362823af82d?raw)
