HelloJohn / docs
API Reference

MFA API

REST API endpoints for enrolling, verifying, and managing multi-factor authentication factors.

MFA API

The MFA API lets you enroll new factors, verify challenges, and manage existing factors for a user.

Factor Object

{
  "id": "fac_01HABCDEF555444",
  "user_id": "usr_01HABCDEF123456",
  "type": "totp",
  "name": "Authenticator App",
  "status": "active",
  "created_at": "2024-01-10T09:00:00Z",
  "last_used_at": "2024-01-20T08:15:00Z"
}

Factor types: totp, sms, email_otp, webauthn, backup_codes

Factor status: pending (enrolled but not yet verified), active, disabled


GET /v1/users/:user_id/mfa/factors

List all MFA factors for a user.

curl "https://api.hellojohn.dev/v1/users/usr_01HABCDEF123456/mfa/factors" \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "X-Tenant-ID: tnt_01HABCDEF654321"

Response:

{
  "data": [
    {
      "id": "fac_01HABCDEF555444",
      "type": "totp",
      "name": "Authenticator App",
      "status": "active",
      "created_at": "2024-01-10T09:00:00Z"
    }
  ],
  "total": 1
}

POST /v1/users/:user_id/mfa/factors

Begin enrolling a new MFA factor. Returns enrollment data (QR code URI for TOTP, etc.).

Body:

FieldTypeRequiredDescription
typestringtotp, sms, email_otp, webauthn
namestringHuman-readable label
phone_numberstringRequired when type is sms
curl -X POST "https://api.hellojohn.dev/v1/users/usr_01HABCDEF123456/mfa/factors" \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "X-Tenant-ID: tnt_01HABCDEF654321" \
  -H "Content-Type: application/json" \
  -d '{"type": "totp", "name": "Authenticator App"}'

Response for TOTP:

{
  "factor_id": "fac_01HABCDEF555444",
  "type": "totp",
  "status": "pending",
  "totp": {
    "secret": "JBSWY3DPEHPK3PXP",
    "uri": "otpauth://totp/HelloJohn:alice%40example.com?secret=JBSWY3DPEHPK3PXP&issuer=HelloJohn",
    "qr_code": "data:image/png;base64,..."
  }
}

Response for SMS:

{
  "factor_id": "fac_01HABCDEF555445",
  "type": "sms",
  "status": "pending",
  "phone_number": "+1***5678"
}

After receiving this response, prompt the user to enter the OTP they received and call the confirm endpoint.


POST /v1/users/:user_id/mfa/factors/:factor_id/confirm

Confirm a pending factor by verifying the first OTP. Activates the factor.

Body:

FieldTypeRequiredDescription
codestringOTP or TOTP code from the factor
curl -X POST "https://api.hellojohn.dev/v1/users/usr_01HABCDEF123456/mfa/factors/fac_01HABCDEF555444/confirm" \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "X-Tenant-ID: tnt_01HABCDEF654321" \
  -H "Content-Type: application/json" \
  -d '{"code": "123456"}'

Response: 200 OK — Returns activated factor object.


DELETE /v1/users/:user_id/mfa/factors/:factor_id

Remove an MFA factor. If the factor is the user's only active factor and the tenant requires MFA, this returns a 403 error.

curl -X DELETE "https://api.hellojohn.dev/v1/users/usr_01HABCDEF123456/mfa/factors/fac_01HABCDEF555444" \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "X-Tenant-ID: tnt_01HABCDEF654321"

Response: 204 No Content


POST /v1/mfa/challenge

Create an MFA challenge during authentication. Called server-side when the sign-in response indicates mfa_required.

Body:

FieldTypeRequiredDescription
factor_idstringThe factor to challenge
challenge_idstringChallenge ID from the mfa_required error
curl -X POST https://api.hellojohn.dev/v1/mfa/challenge \
  -H "Authorization: Bearer pk_live_abc123" \
  -H "X-Tenant-ID: tnt_01HABCDEF654321" \
  -H "Content-Type: application/json" \
  -d '{
    "factor_id": "fac_01HABCDEF555444",
    "challenge_id": "chl_01HABCDEF888777"
  }'

For SMS and email OTP factors, this triggers sending the one-time code.

Response: 200 OK

{
  "challenge_id": "chl_01HABCDEF888777",
  "factor_type": "sms",
  "expires_at": "2024-01-15T10:35:00Z"
}

POST /v1/mfa/verify

Verify an MFA challenge. On success, the user's session is promoted to MFA-verified.

Body:

FieldTypeRequiredDescription
challenge_idstringChallenge ID
factor_idstringFactor ID being verified
codestringOTP or TOTP code
curl -X POST https://api.hellojohn.dev/v1/mfa/verify \
  -H "Authorization: Bearer pk_live_abc123" \
  -H "X-Tenant-ID: tnt_01HABCDEF654321" \
  -H "Content-Type: application/json" \
  -d '{
    "challenge_id": "chl_01HABCDEF888777",
    "factor_id": "fac_01HABCDEF555444",
    "code": "123456"
  }'

Response: 200 OK — Returns new access token with mfa_verified: true.

{
  "access_token": "eyJhbGciOiJFZERTQSJ9...",
  "refresh_token": "rt_01HABCDEF...",
  "expires_in": 3600
}

POST /v1/mfa/backup-codes/generate

Generate a new set of backup codes for a user. This invalidates any previously generated codes.

curl -X POST "https://api.hellojohn.dev/v1/users/usr_01HABCDEF123456/mfa/backup-codes/generate" \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "X-Tenant-ID: tnt_01HABCDEF654321"

Response:

{
  "codes": [
    "ABCD-EFGH",
    "IJKL-MNOP",
    "QRST-UVWX",
    "YZAB-CDEF",
    "GHIJ-KLMN",
    "OPQR-STUV",
    "WXYZ-0123",
    "4567-89AB"
  ],
  "generated_at": "2024-01-15T10:00:00Z"
}

Store these codes securely. They are shown only once.


On this page