CXone Admin API: 400 Bad Request when bulk-updating agent skill proficiencies

We’re trying to automate skill proficiency updates for a group of agents using the CXone Admin API. The goal is to push a JSON payload via a PUT request to /api/v2/users/{userId}/skills to update their proficiencies in bulk.

The documentation implies that we can send an array of skill objects with the new proficiency levels. Here’s the payload structure we’re sending:

[
 {
 "skillId": "abc-123-def",
 "proficiency": 5
 },
 {
 "skillId": "xyz-789-ghi",
 "proficiency": 3
 }
]

We’re hitting a 400 Bad Request with the error message: Invalid request body. Expected object, but got array.

I’ve checked the API specs again, and it seems like the endpoint expects a single object mapping skill IDs to proficiencies, not an array of objects. I tried changing the payload to:

{
 "abc-123-def": 5,
 "xyz-789-ghi": 3
}

This works for a single update, but it’s not scalable for bulk operations. We have hundreds of agents and dozens of skills. Is there a specific endpoint or method for bulk-updating skill proficiencies? Or do we need to loop through each agent and make individual PUT requests?

We’re using the CXone Python SDK, but the issue persists even when making raw HTTP requests. The SDK doesn’t seem to have a bulk-update method for skill proficiencies, which is a bit frustrating.

Has anyone else run into this? We’re looking for a way to update multiple skills for multiple agents without hitting rate limits or making thousands of API calls. Any insights or workarounds would be appreciated. We’re currently stuck on this and need a solution soon.

The endpoint you’re hitting doesn’t actually accept a raw array at the root level. That’s the classic 400 error trap here. The PUT /api/v2/users/{userId}/skills request expects a specific wrapper object containing the skills array, not just the array itself. The API parser sees the leading [ and immediately rejects it because it’s looking for an object {}.

You’ll want to wrap your payload in a skills key. Here’s how the JSON should look:

{
 "skills": [
 {
 "id": "abc-123-def",
 "proficiency": 50
 },
 {
 "id": "xyz-789-ghi",
 "proficiency": 80
 }
 ]
}

Also, double-check your OAuth scopes within the CXone Admin portal. You need the user:skill:write permission assigned to your API integration to successfully push these changes. If you’re using the CXone Python SDK, verify you’ve correctly instantiated the UsersApi client with the proper access token and that the SDK is handling the necessary authentication.

If you’re sticking to raw HTTP calls via requests or similar, make sure you set the Content-Type header to application/json. I’ve seen cases where omitting this header causes the body to be parsed incorrectly, leading to obscure 400 errors.

One more thing: if you’re doing this for a bulk of users, be mindful of CXone’s API rate limits. The API allows a decent throughput, but hammering it with a loop of hundreds of users without delays will likely result in throttling. Implement a simple time.sleep() delay or a promise-based queue with a concurrency limit of 5-10 requests at a time to keep things running smoothly. It’s better to batch them slightly than to hit the limit and have to restart the process. Consider using CXone’s Job Management system for very large updates; it may offer a more solid and scalable solution.

1 Like