PUT /api/v2/conversations/{conversationId}/participants failing to persist custom attributes on active voice calls

What’s the correct approach to sync external CRM fields into participant attributes while a voice conversation is actually ringing? We’ve got the admin UI configured to pull standard routing data, but the external webhook needs to push a few custom key-value pairs directly into the live session.

Here’s the payload we’re sending to PUT /api/v2/conversations/{conversationId}/participants:
{
“id”: “ext:agent-123”,
“type”: “agent”,
“attributes”: {
“crm_ticket_id”: “88421”,
“priority_flag”: “high”
}
}

The call returns a 204 No Content, which looks fine on paper. Checking the conversation object immediately after shows the attributes stuck in the response. Five minutes later, pulling the same endpoint via GET just wipes them clean. The admin UI doesn’t show them in the participant details pane either.

We tried switching to the PATCH method with the exact same JSON structure. Got a 409 Conflict back complaining about version mismatches. Bumped the version field in the request header to match the latest GET response, but the 409 just keeps rolling.

List of things attempted so far:

  • Verified the external system OAuth token has conversation:read and conversation:write scopes.
  • Checked the Architect flow to ensure no attribute override actions are firing on transfer.
  • Tested with a mock conversation using the Developer Portal sandbox. Attributes stick there, but only for about sixty seconds before vanishing.
  • Tried appending the attributes directly to the routing object instead of the participant level. Same result.

The documentation mentions attribute persistence depends on the conversation state, but we’re hitting this on calls that are firmly in the connected state. Is there a specific header or SDK method we’re missing to lock these values in place? Or does the platform just overwrite external attribute pushes once the call bridges to the agent queue?

Waiting on a response from the routing team, but figured I’d drop the exact payload and error codes here first. The sandbox behavior is throwing us off completely.