Set the caller's primary phone number
Starts making a validated phone number the caller's global primary phone number and sends the first OTP; the returned otp tells where it went. While an OTP is pending, a new call sends no other OTP: use otp/send. With no non-closed current account, one OTP authorizes the change through the existing validated primary contact. With one, the change instead uses that current account to verify the owner and the new number; the primary changes only after the account provider confirms it, and pending onboarding is exposed as resumable status. A user token calls this on behalf of the signed-in user. Returns 404 when the phone number is not a contact on the caller's profile, and 409 when a previous primary-contact change is still synchronizing.
Authorization
oauth2 Create a client with POST /v1/oauth/clients, then exchange its credentials for an access token. AUTHORIZATION_CODE clients act on behalf of a signed-in user and always use PKCE; CLIENT_CREDENTIALS clients act as themselves. A client may only be granted scopes its creator already holds — read the grantable list from GET /v1/oauth/clients/scopes.
In: header
Request Body
application/json
The validated phone number to make the caller's primary phone number. When authorization_channel is omitted, the owner-authorization OTP goes to the caller's current primary contact of the target type.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/user/settings/phones/primary" \ -H "Content-Type: application/json" \ -d '{ "phone_number": "+254712345678" }'{ "status": "OTP_REQUIRED", "contact": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "value": "jane@example.com", "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "is_primary": true, "validated": true, "validated_at": "2026-01-15T09:20:00Z", "requires_verification": true, "verification_locked": true, "verification_locked_at": "2026-01-20T16:45:00Z", "created_at": "2026-09-01T10:15:30Z" }, "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "otp_url": "/v1/user/settings/emails/primary/otp/send", "confirm_url": "/v1/user/settings/emails/primary/otp/confirm", "status_url": "/v1/user/settings/emails/primary/change", "identity_sync_pending": true}{ "status": "OTP_REQUIRED", "contact": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "value": "jane@example.com", "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "is_primary": true, "validated": true, "validated_at": "2026-01-15T09:20:00Z", "requires_verification": true, "verification_locked": true, "verification_locked_at": "2026-01-20T16:45:00Z", "created_at": "2026-09-01T10:15:30Z" }, "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "otp_url": "/v1/user/settings/emails/primary/otp/send", "confirm_url": "/v1/user/settings/emails/primary/otp/confirm", "status_url": "/v1/user/settings/emails/primary/change", "identity_sync_pending": true}{ "code": "BAD_REQUEST", "description": "Bad Request"}{ "code": "UNAUTHORISED", "description": "Unauthorised"}{ "code": "FORBIDDEN", "description": "Forbidden"}{ "status": "OTP_REQUIRED", "contact": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "value": "jane@example.com", "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "is_primary": true, "validated": true, "validated_at": "2026-01-15T09:20:00Z", "requires_verification": true, "verification_locked": true, "verification_locked_at": "2026-01-20T16:45:00Z", "created_at": "2026-09-01T10:15:30Z" }, "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "otp_url": "/v1/user/settings/emails/primary/otp/send", "confirm_url": "/v1/user/settings/emails/primary/otp/confirm", "status_url": "/v1/user/settings/emails/primary/change", "identity_sync_pending": true}{ "status": "OTP_REQUIRED", "contact": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "value": "jane@example.com", "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "is_primary": true, "validated": true, "validated_at": "2026-01-15T09:20:00Z", "requires_verification": true, "verification_locked": true, "verification_locked_at": "2026-01-20T16:45:00Z", "created_at": "2026-09-01T10:15:30Z" }, "otp": { "step": "OWNER_AUTHORIZATION", "sent_to": "EMAIL", "steps": 2, "digits": 6 }, "otp_url": "/v1/user/settings/emails/primary/otp/send", "confirm_url": "/v1/user/settings/emails/primary/otp/confirm", "status_url": "/v1/user/settings/emails/primary/change", "identity_sync_pending": true}{ "code": "RATE_LIMIT_EXCEEDED", "description": "Too Many Requests"}{ "code": "INTERNAL_ERROR", "description": "Internal Server Error"}Send the phone OTP POST
Sends or resends the verification OTP for a pending phone number. A user token calls this on behalf of the signed-in user. The returned `otp` tells the step that the OTP confirms and where it went. Returns 404 when the contact does not exist, and 503 when the carrier lookup is temporarily unavailable.
Get the primary phone number change GET
Returns the status of the caller's active primary-phone-number change, for resuming after an interrupted flow. A user token calls this on behalf of the signed-in user. Returns 404 when the caller has no active primary-phone-number change.