Cowdidocs
API ReferenceContact details

Set the caller's primary phone number

POST
/v1/user/settings/phones/primary

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.

AuthorizationBearer <token>

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"}