PATCH /api/v2/conversations/calls/{id} participant disconnect returning 409

Trying to programmatically drop a specific participant from a conference bridge using the Conversations API. We’ve got a Terraform setup that provisions the infrastructure, but the runtime control is handled via direct REST calls from a Node service.

I’m sending a PATCH to /api/v2/conversations/calls/{conversationId} with the following body:

{
 "participants": [
 {
 "participantId": "abc-123",
 "action": "disconnect"
 }
 ]
}

The call returns a 409 Conflict with the message “Participant is not in the conversation.” This is weird because I just queried the conversation details and that participantId is definitely listed there with status connected.

I’ve double-checked the token has conversation:call:write scopes. Is there a race condition where the state changes between the GET and the PATCH? Or am I missing a required field in the disconnect action payload? The docs are pretty sparse on conference sub-actions.

Here’s the curl equivalent that fails:

curl -X PATCH "https://api.mypurecloud.com/api/v2/conversations/calls/xyz-456" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"participants": [{"participantId": "abc-123", "action": "disconnect"}]}'

Any ideas on why the ID isn’t recognized?

You’re hitting a 409 because the participantId in your payload is likely the external ID or a custom field, not the internal system ID generated when the participant joins. The API is strict about this. You need the actual id from the participants array of the conversation object.

Here’s how I usually handle this in Node.js. First, fetch the conversation to get the correct ID, then patch it.

const platformClient = require('@genesyscloud/genesyscloud-node-sdk');
const api = platformClient.ConversationsApi;

async function disconnectParticipant(conversationId, targetExternalId) {
 // 1. Get the conversation to find the internal participant ID
 const conv = await api.getConversation(conversationId);
 const participant = conv.participants.find(p => p.externalId === targetExternalId);
 
 if (!participant) throw new Error('Participant not found');

 // 2. Perform the disconnect using the internal ID
 await api.patchConversation({
 conversationId: conversationId,
 body: {
 participants: [{
 id: participant.id, // Crucial: use the system ID
 action: "disconnect"
 }]
 }
 });
}

Make sure your OAuth token has the conversation:write scope. Also, if the participant is already disconnected, you’ll get a 409, so wrap that in a try-catch.