Summary and recommendation
NICE CXone exposes user lifecycle operations through its Admin API using OAuth 2.0 client credentials. A critical naming caveat applies immediately: agent-type users are managed under the `/agents` resource path, not `/users`-sending requests to a `/users` endpoint will not behave as expected.
The base URL encodes both a region code (e.g., `na1`, `eu1`) and an explicit API version number (e.g., `v28.0`); both must match your tenant's configuration or requests return 404 or auth errors.
For identity graph use cases-syncing user state across an IdP, HRIS, and CXone-SCIM 2.0 is the preferred integration surface, but it is gated to Enterprise tier and requires SSO (SAML or OIDC) to be fully active before the SCIM token can be generated.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (client credentials or authorization code flow) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: OAuth 2.0 (client credentials or authorization code flow)
Setup steps
- Log in to NICE CXone as an administrator and navigate to Admin > API Applications.
- Create a new API application to obtain a Client ID and Client Secret.
- Assign the application the required permission scopes for user management.
- POST to the token endpoint: https://api-{region}.niceincontact.com/incontactapi/services/v{version}/token with grant_type=client_credentials, client_id, and client_secret.
- Receive an access_token (Bearer) and token_type in the response; use the Bearer token in the Authorization header for all subsequent API calls.
- Tokens expire; implement refresh logic using the returned expires_in value.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| AdminAPI | Grants access to administrative operations including user create, read, update, and delete. | All user management endpoints |
| ReadUsers | Read-only access to user records and user lists. | GET /users, GET /users/{userId} |
| WriteUsers | Create and update user records. | POST /users, PUT /users/{userId}, PATCH /users/{userId} |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| userId | integer | Unique internal identifier for the user. | system-generated | read-only | Used as path parameter in user-specific endpoints |
| userName | string | Login username for the user. | required | optional | Must be unique within the tenant |
| firstName | string | User's first name. | required | optional | |
| lastName | string | User's last name. | required | optional | |
| string | User's email address. | required | optional | Used for notifications and SSO matching | |
| password | string | User's password (write-only). | required (if not SSO) | optional | Not returned in GET responses |
| isActive | boolean | Whether the user account is active. | optional (default: true) | optional | Set to false to deactivate without deleting |
| teamId | integer | ID of the team the user belongs to. | optional | optional | Teams are pre-configured in the tenant |
| profileId | integer | Security profile (role) assigned to the user. | required | optional | Controls permissions within CXone |
| skillIds | array[integer] | List of skill IDs assigned to the user. | optional | optional | Skills determine routing eligibility |
| reportToId | integer | userId of the user's manager. | optional | optional | |
| location | string | Physical or logical location of the user. | optional | optional | |
| timeZone | string | User's time zone (IANA format). | optional | optional | |
| countryId | integer | Country identifier for the user. | optional | optional | |
| phoneNumber | string | User's phone number. | optional | optional | |
| ntLoginName | string | Windows/AD login name for SSO mapping. | optional | optional | Required when using Active Directory SSO |
| federatedId | string | External identity provider subject identifier. | optional | optional | Used for SAML/OIDC SSO federation |
| hireDate | string (ISO 8601) | User's hire date. | optional | optional | |
| notes | string | Free-text notes about the user. | optional | optional | |
| lastLogin | string (ISO 8601) | Timestamp of the user's last login. | system-generated | read-only |
Core endpoints
List Users
- Method: GET
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/agents - Watch out for: The endpoint path uses 'agents' not 'users' for agent-type users. Administrative users may be under a separate endpoint.
Request example
GET /incontactapi/services/v28.0/agents?top=50&skip=0
Authorization: Bearer {access_token}
Response example
{
"resultSet": {
"agents": [
{"agentId": 1234, "userName": "jdoe", "firstName": "Jane", "lastName": "Doe", "isActive": true}
]
},
"totalRecords": 1
}
Get User by ID
- Method: GET
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/agents/{agentId} - Watch out for: agentId is the internal integer ID, not the userName. Ensure you resolve the correct ID before calling.
Request example
GET /incontactapi/services/v28.0/agents/1234
Authorization: Bearer {access_token}
Response example
{
"resultSet": {
"agents": [
{"agentId": 1234, "userName": "jdoe", "email": "jdoe@example.com", "profileId": 5, "teamId": 10, "isActive": true}
]
}
}
Create User
- Method: POST
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/agents - Watch out for: profileId (security profile) is required. Creating a user without a valid profileId returns a 400 error.
Request example
POST /incontactapi/services/v28.0/agents
Authorization: Bearer {access_token}
Content-Type: application/json
{"firstName":"Jane","lastName":"Doe","userName":"jdoe","email":"jdoe@example.com","profileId":5,"teamId":10}
Response example
{
"resultSet": {
"agentId": 1234
}
}
Update User
- Method: PUT
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/agents/{agentId} - Watch out for: PUT replaces the full user object. Omitting optional fields may reset them to defaults. Use with caution.
Request example
PUT /incontactapi/services/v28.0/agents/1234
Authorization: Bearer {access_token}
Content-Type: application/json
{"firstName":"Jane","lastName":"Smith","isActive":true}
Response example
{
"resultSet": {
"agentId": 1234
}
}
Deactivate User
- Method: DELETE
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/agents/{agentId} - Watch out for: DELETE typically deactivates (sets isActive=false) rather than permanently deleting the user record. Permanent deletion may require a separate admin action.
Request example
DELETE /incontactapi/services/v28.0/agents/1234
Authorization: Bearer {access_token}
Response example
HTTP 200 OK
{}
Assign Skills to User
- Method: POST
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/agents/{agentId}/skills - Watch out for: Skills must exist in the tenant before assignment. Proficiency is typically 1–20 depending on tenant configuration.
Request example
POST /incontactapi/services/v28.0/agents/1234/skills
Authorization: Bearer {access_token}
Content-Type: application/json
{"skills":[{"skillId":101,"proficiency":5}]}
Response example
HTTP 200 OK
{}
Get User Teams
- Method: GET
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/teams - Watch out for: Retrieve teamId values here before creating or updating users; teamId is required for team assignment.
Request example
GET /incontactapi/services/v28.0/teams
Authorization: Bearer {access_token}
Response example
{
"resultSet": {
"teams": [
{"teamId": 10, "teamName": "Support Team A", "isActive": true}
]
}
}
Get Security Profiles
- Method: GET
- URL:
https://api-{region}.niceincontact.com/incontactapi/services/v{version}/security-profiles - Watch out for: profileId is required when creating users. Fetch this list to map role names to IDs before provisioning.
Request example
GET /incontactapi/services/v28.0/security-profiles
Authorization: Bearer {access_token}
Response example
{
"resultSet": {
"securityProfiles": [
{"profileId": 5, "profileName": "Agent", "isActive": true}
]
}
}
Rate limits, pagination, and events
- Rate limits: NICE CXone enforces API rate limits per tenant. Exact published limits are not publicly documented; limits are enforced at the platform level and vary by tenant configuration.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: HTTP 429 is returned when limits are exceeded. Retry-After header presence is not confirmed in official docs. Implement exponential backoff.
- Pagination method: offset
- Default page size: 50
- Max page size: 100
- Pagination pointer: skip / top
| Plan | Limit | Concurrent |
|---|---|---|
| All plans | Not publicly specified; contact NICE support for tenant-specific limits | 0 |
- Webhooks available: No
- Webhook notes: NICE CXone does not expose a native outbound webhook system for user lifecycle events (create, update, deactivate) via the public Admin API. Event-driven integrations typically rely on polling or the CXone Studio/Flow automation engine.
- Alternative event strategy: Use scheduled polling of GET /agents to detect changes, or leverage SCIM provisioning push from an IdP (Okta, Entra ID) for event-driven user lifecycle management.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://api-{region}.niceincontact.com/incontactapi/scim/v2
Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}, GET /Groups, POST /Groups, PATCH /Groups/{id}
Limitations:
- Requires SSO (SAML or OIDC) to be configured before SCIM can be enabled.
- Only available on Enterprise pricing tier.
- SCIM Groups map to CXone Teams; not all team attributes are writable via SCIM.
- Custom CXone attributes (e.g., skillIds, profileId) may require supplemental Admin API calls after SCIM provisioning.
- SCIM token is a long-lived bearer token generated in the CXone admin UI; OAuth 2.0 client credentials are not used for SCIM.
Common scenarios
Provisioning a new agent requires three pre-flight lookups before the creation POST: resolve a valid profileId from GET /security-profiles, resolve a valid teamId from GET /teams, and confirm target skills exist in the tenant.
The creation call (POST /agents) accepts name strings for display fields but requires integer IDs for profileId and teamId-passing role or team names directly returns a 400. Skill assignment is a separate call (POST /agents/{agentId}/skills) and cannot be bundled into the creation payload.
For deactivation, DELETE /agents/{agentId} sets isActive=false rather than permanently removing the record; permanent deletion is not available via API and requires a manual admin action in the UI.
When syncing from Okta or Entra ID via SCIM, SCIM Groups map to CXone Teams, but attributes outside the SCIM schema-specifically skillIds and profileId-must be written via supplemental Admin API calls after the SCIM provisioning event completes.
Provision a new agent via Admin API
- Authenticate: POST to token endpoint with client_credentials to obtain Bearer token.
- Fetch valid profileId: GET /security-profiles and identify the correct agent profile.
- Fetch valid teamId: GET /teams and identify the target team.
- Create user: POST /agents with firstName, lastName, userName, email, profileId, teamId.
- Assign skills: POST /agents/{agentId}/skills with required skillId and proficiency values.
- Verify: GET /agents/{agentId} to confirm all fields are set correctly.
Watch out for: profileId and teamId must be resolved before creation; the API does not accept name strings for these fields.
Deactivate a departing employee
- Authenticate: obtain a fresh Bearer token.
- Resolve agentId: GET /agents?userName=jdoe to find the internal agentId.
- Deactivate: DELETE /agents/{agentId} (sets isActive=false).
- If using SCIM: PATCH /scim/v2/Users/{scimId} with {"active": false} to sync deactivation from IdP.
Watch out for: DELETE deactivates rather than permanently removes the user. Historical reporting data is retained. Permanent deletion requires manual admin action in the CXone UI.
Sync users from Okta via SCIM
- Confirm Enterprise plan and SSO (SAML/OIDC) are active on the CXone tenant.
- In CXone Admin UI, navigate to Security > SCIM and generate a SCIM bearer token.
- In Okta, add the NICE CXone SCIM application and configure the SCIM base URL and bearer token.
- Map Okta profile attributes to SCIM User attributes (userName → email, given_name, family_name).
- Assign the Okta application to users/groups; Okta will POST /scim/v2/Users for each assigned user.
- For attributes not covered by SCIM (e.g., skillIds), use a supplemental Admin API call triggered post-provisioning.
Watch out for: SCIM Groups map to CXone Teams but not all team properties are writable via SCIM. Skill assignments must be handled separately via the Admin API.
Why building this yourself is a trap
The most common integration failure is using PUT /agents/{agentId} for partial updates: PUT is a full object replacement, so omitting any optional field resets it to its default value. Always fetch the current user object and merge your changes before issuing a PUT.
A second trap is token lifecycle: OAuth 2.0 access tokens are short-lived, and expired tokens return 401 with no automatic retry signal-implement proactive refresh keyed to the expires_in value rather than reacting to 401s.
Rate limits are not publicly documented; the API returns HTTP 429 when limits are exceeded, but no Retry-After header is confirmed in official documentation, so exponential backoff is the only safe retry strategy.
Finally, SCIM uses a separate long-lived bearer token generated in the CXone Admin UI-it does not use the OAuth 2.0 client credentials flow, so your SCIM and Admin API auth paths must be managed independently.
Automate NICE CXone workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.