Sending this JSON to PATCH /api/v2/conversations/voice/{id}/participants/{participantId} returns 200 but the attributes never stick.
{ "attributes": { "extSysId": "9921", "routing": { "skill": "support" } } }
The response body shows the updated object, yet a subsequent GET request pulls the old data. We’re using the JS SDK v2.14. Is there a specific scope required beyond conversation:write or are custom attributes ignored on voice participants?
The issue isn’t the scope. conversation:write is enough for standard participant updates. The problem is how you’re structuring the routing object inside the payload.
Genesys Cloud treats routing as a reserved top-level key on the participant object, not a nested property inside attributes. When you wrap it like that, the API accepts the JSON (hence the 200) but ignores the routing data during persistence because it doesn’t match the expected schema path. You also can’t write arbitrary custom attributes directly into the participant record via PATCH in the same way you can for web or chat. Voice participants are stricter.
Here is the correct structure for updating a routing skill:
{
"routing": {
"skill": "support",
"group": null,
"language": null,
"wrapUpCode": null
}
}
Notice routing is at the root level of the PATCH body, not inside attributes. Also, make sure you’re including all required fields if the schema expects them, or use null for optional ones you don’t want to change.
If you truly need custom data on a voice participant, you usually have to map it to an existing field or use a separate system to track it by participantId. The API won’t store a free-form extSysId string on a voice participant object the way it does for web messaging.
Try splitting the call. One PATCH for routing, and if you need metadata, log it externally or use a queue member attribute if you’re routing to a specific queue. Don’t mix custom key-value pairs with routing logic in the same payload block. It confuses the parser.