Enrollment

SMS is implicit — every user has a verified mobile number from onboarding and can receive an SMS challenge without any extra setup. TOTP and passkey are explicit per-user enrolments that the partner triggers when the user opts in. Both factors must appear in the application's allowedScaMethods or the enrolment endpoints return 31099.

TOTP

POST /user/start/setup-two-factor
Authorization: <HMAC>

{ "userId": "<uuid>" }

Returns { secret, qrCode }. Render the QR code in the user's browser or surface the secret manually so they can add it to their authenticator app.

POST /user/resume/setup-two-factor
Authorization: <HMAC>

{
  "userId": "<uuid>",
  "secret": "<value returned above>",
  "code": "123456"
}

code is the current 6-digit code from the authenticator. On success the secret is bound to the user.

Passkey (WebAuthn)

POST /user/passkey/register/start
Authorization: <HMAC>

{ "userId": "<uuid>" }

Returns the WebAuthn PublicKeyCredentialCreationOptions shape (challenge, rp, user, pubKeyCredParams, …). Pass it to navigator.credentials.create() in the browser.

POST /user/passkey/register/complete
Authorization: <HMAC>

{
  "userId": "<uuid>",
  "origin": "https://app.example.com",
  "credential": { /* PublicKeyCredential serialised */ }
}

The origin must match the page that ran the ceremony. List and remove enrolled passkeys with POST /user/passkey/list and POST /user/passkey/delete (body: { userId, credentialId }).