1. Docs
  2. API Reference
  3. Complete TOTP factor enrollment

Complete TOTP factor enrollment

POST/v1/identity/auth/mfa/totp/enroll/verify

Authentication

  • Bearer Token Authorization

    JWT access token

Request body

  • enrollment_tokenstring*

    The sealed enrollment_token returned by the `start` endpoint. Carries the provisional TOTP secret server-side.

  • codestring*

    6-digit code from the authenticator app.

  • labelstring*

    User-facing nickname for the factor ("iPhone 15", "Work Laptop"). Shown on the factor-management surface.

Code samples

cURLJavaScriptPythonGo
curl -X POST "https://api.canopy.dev/v1/identity/auth/mfa/totp/enroll/verify" \
  -H "Authorization: Bearer $CANOPY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enrollment_token": "string",
    "code": "123456",
    "label": "iPhone 15"
  }'

Responses

200 Verifies a 6-digit code against the secret carried by the enrollment token and persists the factor. Returns the 10 single-use recovery codes — this is the only time they are shown in plaintext, and only on the first factor enrollment (subsequent enrollments return `recovery_codes: null` so the existing batch stays valid).
{
  "factor": {
    "id": "00000000-0000-0000-0000-000000000000",
    "type": "totp",
    "label": "iPhone 15",
    "enrolled_at": "2026-04-20T12:00:00.000Z",
    "last_used_at": "2026-04-20T12:00:00.000Z"
  },
  "recovery_codes": [
    "ABCD-EFGH-IJKL-MNOP",
    "QRST-UVWX-YZ23-4567",
    "..."
  ],
  "recovery_codes_generation": 0
}

application/json

  • factorMfaFactorResponseDto*
  • recovery_codesstring[]*

    Ten single-use recovery codes. **Shown exactly once** — the server only stores hashes. Display, allow copy/download, and proceed only after the user confirms they have saved them.

  • recovery_codes_generationnumber*

    Monotonically-increasing generation number for this batch. Used by the portal admin surface to show "X of 10 remaining" against the current batch.

400 Enrollment token is missing, malformed, or expired
401 Invalid or expired token
403 This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa)

Returned object

On this page

Related endpoints

GETList enrolled MFA factors for the caller
POSTBegin TOTP factor enrollment
POSTBegin WebAuthn factor enrollment
POSTComplete WebAuthn factor enrollment
POSTProve a fresh factor to authorise a sensitive MFA mutation
POSTBegin a WebAuthn-backed step-up ceremony
POSTComplete a WebAuthn step-up ceremony
DELETERemove an enrolled MFA factor
POSTRegenerate the identity's single-use recovery codes
GETList the identity's active 'remember this device' records
DELETERevoke every trusted device for the caller
DELETERevoke a single trusted device