CXone Admin API bulk update agent skills 400 error

Does anyone know why this fails? I am trying to bulk update agent skill proficiencies.

Background

I am using the CXone Admin API to update multiple agents at once.

Issue

The docs state “bulk operations accept an array of update requests.” I sent this payload:

POST /api/v2/users/skills/bulkUpdate
[
 { "userId": "123", "skillId": "456", "proficiency": "Expert" }
]

I get a 400 Bad Request.

Troubleshooting

Docs say the format is correct. Why does it fail?

1 Like

If I recall correctly, the 400 error often stems from missing the id field in the nested skill objects. The API requires explicit identification for each skill update within the array.

{
 "items": [
 {
 "userId": "user-uuid",
 "skills": [
 { "id": "skill-uuid", "proficiency": 5 }
 ]
 }
 ]
}

Note: Ensure you use client_credentials scope admin:agent:write.

3 Likes

The documentation actually says bulk endpoints are deprecated for skill assignments. Use the standard single-user endpoint in a loop within n8n.

  • Validate skill UUIDs exist.
  • Use admin:agent:write scope.
  • Implement retry logic for rate limits.
PUT /api/v2/users/{userId}/routingskills/{skillId}
Content-Type: application/json
{"proficiency": 5}

The problem here is… well, it’s a bit of a mess. The suggestion about the id field is technically correct for the payload structure, but if you are hitting a 400 on a “bulk” endpoint, you are likely targeting a deprecated or non-existent path. CXone does not have a single /api/v2/users/bulk endpoint for skill updates that accepts that JSON structure. You have to iterate.

In my Laravel apps, I use Guzzle to handle this because the PHP SDK can be sluggish with large arrays. You need to fetch the user list first, then loop through them with PATCH /api/v2/users/{userId}/skills. Cache your OAuth token to avoid rate limiting yourself. Here is how I structure the request body for each user:

// Inside your loop for each user
$payload = [
 'skills' => [
 [
 'skillId' => 'skill-uuid-here',
 'proficiency' => 5
 ]
 ]
];

$client->patch("/api/v2/users/{$userId}/skills", [
 'json' => $payload,
 'headers' => [
 'Authorization' => 'Bearer ' . $accessToken,
 'Content-Type' => 'application/json'
 ]
]);

Don’t forget to validate the skill UUIDs exist before sending. The API is strict. Double-check the skill UUIDs in the Admin UI to ensure they match what you’re passing in the payload. Also, verify you have the correct permissions assigned to the API user to modify user skills.

It depends, but typically the bulk endpoint is deprecated for this specific operation, so I switched to iterating PUT /api/v2/users/{userId}/routingskills/{skillId} with the payload below.

{
 "id": "skill-uuid",
 "proficiency": 5
}