Begin TOTP factor enrollment
/v1/identity/auth/mfa/totp/enroll/startAuthentication
- Bearer Token
AuthorizationJWT access token
Code samples
curl -X POST "https://api.canopy.dev/v1/identity/auth/mfa/totp/enroll/start" \ -H "Authorization: Bearer $CANOPY_TOKEN"
const response = await fetch("https://api.canopy.dev/v1/identity/auth/mfa/totp/enroll/start", {
method: "POST",
headers: {
"Authorization": "Bearer $CANOPY_TOKEN"
},
});
const data = await response.json();import requests
response = requests.post(
"https://api.canopy.dev/v1/identity/auth/mfa/totp/enroll/start",
headers={
"Authorization": "Bearer $CANOPY_TOKEN"
},
)
data = response.json()package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.canopy.dev/v1/identity/auth/mfa/totp/enroll/start", nil)
req.Header.Set("Authorization", "Bearer $CANOPY_TOKEN")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
200 Generates a provisional TOTP secret, the `otpauth://` URI for the authenticator app, a base64 QR data URL, and a short-lived sealed `enrollment_token` that must be returned to the verify endpoint within 5 minutes. The factor row is NOT created until verify succeeds.
{
"enrollment_token": "string",
"otpauth_uri": "otpauth://totp/MFA:alice%40acme.com?secret=JBSWY3DPEHPK3PXP&issuer=MFA",
"manual_entry_key": "JBSWY3DPEHPK3PXP"
}application/json
enrollment_token *stringOpaque short-lived (5 min) sealed token carrying the provisional TOTP secret. Must be returned to the verify endpoint exactly.
otpauth_uri *string`otpauth://totp/...` URI for the authenticator app to import. Render as a QR code on the client.
manual_entry_key *stringHuman-readable fallback for users who can't scan the QR. Base32-encoded secret string.
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)