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. You need user:skill:write to push these changes. If you’re using the Genesys Cloud Platform SDK for Node.js, it handles this wrapping automatically if you use the putUserSkills method from the UsersApi class. You just pass the array of skill objects to the method, and the SDK constructs the proper UserSkills entity under the hood. It saves the headache of manually formatting the wrapper.
If you’re sticking to raw HTTP calls via curl or axios, make sure you set the Content-Type header to application/json. I’ve seen cases where missing this header causes the body to be parsed incorrectly, leading to obscure 400s.
One more thing: if you’re doing this for a bulk of users, be mindful of the rate limits. The API allows a decent throughput, but hammering it with a loop of 1000 users without delays will get you throttled. A simple setTimeout or a promise-based queue with a concurrency limit of 5-10 requests at a time usually keeps things smooth. It’s better to batch them slightly than to hit the limit and restart.