PATCH /api/v2/conversations/voice/{id}/participants/{id} returns 400 when transferring to a queue

I’m trying to programmatically transfer an active voice conversation to a different queue using the Conversations API. We have a custom client app that handles specific routing logic based on customer attributes pulled from our CRM. The goal is to move the participant to a ‘Premium Support’ queue without dropping the call or requiring the agent to manually click transfer.

I’ve been testing this against PATCH /api/v2/conversations/voice/{conversationId}/participants/{participantId}. The documentation suggests I can update the routing object to change the queue. Here is the payload I’m sending:

{
 "routing": {
 "queueId": "12345678-abcd-1234-abcd-123456789012",
 "wrapUpCode": null
 }
}

The request returns a 400 Bad Request with the message Invalid routing configuration. I’ve double-checked that the queueId is valid and that the user making the API call has the conversation:view and routing:queue permissions. I can successfully update other participant properties like customAttributes using the same endpoint, so authentication and basic connectivity aren’t the issue.

I tried adding transferType: "consultative" and transferType: "blind" to the routing object, but that didn’t help. I also checked the transfer object in the response of a GET request, but it seems read-only for this endpoint. Is there a specific field I’m missing in the routing object? Or is this endpoint simply not designed for queue transfers and I need to use a different API like /transfer?

Any insights would be appreciated. I’m stuck on this one.

The 400 error usually stems from the routingData structure inside the participant update payload. You can’t just pass a queue ID directly. The API expects a specific object shape that includes the workType and the target configuration.

Here is the exact JSON structure you need for the body of that PATCH request. Note the workType must match the queue’s type, and the target object requires the id of the queue.

{
 "routingData": {
 "workType": "voice",
 "queue": {
 "id": "your-premium-support-queue-id"
 },
 "skills": [],
 "wrapUpCode": {
 "id": null
 }
 },
 "state": "queued",
 "monitoringState": null,
 "conferenceState": null,
 "disposition": null,
 "hold": false,
 "mute": false,
 "holdTime": null,
 "wrapUp": null,
 "transfer": null
}

Make sure you set "state": "queued". If you leave it as "connected" or don’t include it, the platform won’t know how to re-route the call. Also, verify that the participant you are updating actually has the permissions to be transferred to that specific queue. If the agent lacks the conversation:view or specific queue transfer rights, you’ll get a 400 or 403.

I ran into this same issue when building a sync job for our New Relic dashboards. We were trying to capture queue wait times for specific customer tiers. The fix was ensuring the routingData object was fully formed, not just a snippet.

One thing to watch out for: if the call is already in a conference, you might need to handle the conferenceState as well. But for a simple transfer, the payload above should work. Check your Genesys Cloud logs if it still fails. The error message there is usually more descriptive than the HTTP status code.

Spot on. The routingData structure is the key. I ran into the same 400 last week. You also need to ensure the wrapupCode is null or omitted, otherwise the API thinks you’re trying to end the interaction instead of transferring it.