PATCH /api/v2/conversations/calls fails to transfer to queue with 400

Struggling to figure out why the PATCH request to update conversation participants returns a 400 Bad Request when attempting to transfer a call to a queue. The payload structure matches the OpenAPI spec for adding a participant with a queue address, but the API rejects it immediately.

{
 "to": {
 "address": "queue:00000000-0000-0000-0000-000000000000",
 "type": "queue"
 },
 "type": "transfer"
}

The error response is generic with no specific validation details. Is the transfer type field deprecated or is there a specific header required for queue transfers?

Make sure you verify the participant address format and the conversation state before sending the PATCH request. The 400 Bad Request error often stems from subtle schema mismatches or invalid target states rather than the payload structure itself. When transferring a call to a queue, the to object must strictly adhere to the ParticipantAddress schema.

Here is the corrected approach using the PureCloudPlatformClientV2 SDK in Python, which handles the serialization and validation for you:

  1. Initialize the client with valid OAuth tokens.
  2. Construct the transfer participant object explicitly.
  3. Execute the patch with the correct endpoint.
from purecloudplatformclientv2 import PlatformApiClient, ConversationApi

# Initialize API client
api_client = PlatformApiClient()
conversation_api = ConversationApi(api_client)

# Define the target queue address
queue_id = "00000000-0000-0000-0000-000000000000"
queue_address = f"queue:{queue_id}"

# Create the participant update body
# Note: The 'type' must be 'transfer' and 'to' must be a valid ParticipantAddress
transfer_body = {
 "to": {
 "address": queue_address,
 "type": "queue"
 },
 "type": "transfer"
}

try:
 # Patch the conversation participant
 # conversation_id and participant_id are required
 response = conversation_api.patch_conversations_conversation_participant(
 conversation_id="your-conversation-id",
 participant_id="your-participant-id",
 body=transfer_body
 )
 print("Transfer initiated successfully.")
except Exception as e:
 print(f"Error: {e}")

Key Requirements:

Parameter Requirement
address Must follow queue:{queue-id} format exactly.
type Must be queue for the inner object, transfer for the outer.
Conversation State The call must be connected or routing.

Ensure your OAuth token includes the conversation:write scope. If the issue persists, check the Architect flow to ensure the transfer is permitted at the orchestration level.

It depends, but typically the issue isn’t the payload structure but the missing from participant ID in the request body. The suggestion above correctly highlights the ParticipantAddress schema, but the API also requires you to specify which participant is initiating the transfer. Without the from field, the server cannot map the action to an active leg, resulting in a 400 error. When integrating with ServiceNow via Platform API, I always ensure the from object includes the exact id of the participant performing the transfer. Here is the complete working payload structure:

{
 "from": {
 "id": "participant-id-from-active-leg"
 },
 "to": {
 "address": "queue:00000000-0000-0000-0000-000000000000",
 "type": "queue"
 },
 "type": "transfer"
}

Double-check that the conversation is in an active state and that the calling party has the routing:conversation:transfer scope. I’ve seen this exact failure mode when the token lacks explicit transfer permissions, even if the queue read scope is present.

If you check the docs, they mention the transfer action requires explicit participant identification, which is missing in the current payload.

Cause:
The OpenAPI spec for POST /api/v2/conversations/calls/{conversationId}/participants mandates a from field to identify the initiating participant. The generator validates the schema strictly, and omitting from causes a 400 error because the server cannot determine which conversation leg to modify.

Solution:
Include the from participant ID in the request body. Use the following JSON structure:

{
 "from": {
 "id": "existing-participant-uuid"
 },
 "to": {
 "address": "queue:00000000-0000-0000-0000-000000000000",
 "type": "queue"
 },
 "type": "transfer"
}

Ensure the from.id matches an active participant in the conversation. This aligns with the AddParticipantRequest model definition in the spec.

The problem here is the missing from participant ID. The API needs to know which leg to modify.

  1. Add "from": { "id": "<participant_id>" } to your payload.
  2. Use the JS SDK to avoid manual JSON construction errors.
const body = new PlatformClient.ConvApi.UpdateConversationParticipantRequest({
 from: { id: participantId },
 to: { address: queueAddress, type: "queue" },
 type: "transfer"
});
await platformClient.conversations.updateConversationParticipantCall(conversationId, participantId, body);