How to disconnect a specific participant from a conference call using the Conversations API

How to disconnect a specific participant from a conference call using the Conversations API

What You Will Build

  • You will write a script that programmatically terminates a specific participant’s connection to an active Genesys Cloud conversation.
  • You will use the Genesys Cloud Conversations API (POST /api/v2/conversations/{conversationId}/participants/{participantId}/actions/participant/leave) via the Python SDK.
  • This tutorial covers Python 3.9+ with the genesyscloud SDK.

Prerequisites

  • OAuth Client Type: Machine-to-Machine (JWT) or Client Credentials.
  • Required Scopes: conversation:participant:write is mandatory to execute participant actions. conversation:read may be needed if you are dynamically discovering participant IDs.
  • SDK Version: genesyscloud Python SDK v1.1.0 or later.
  • Runtime: Python 3.9 or higher.
  • Dependencies:
    pip install genesyscloud
    

Authentication Setup

Genesys Cloud APIs require a valid JWT or OAuth access token. For server-side scripts, the Client Credentials flow is the most robust approach because it handles token refresh automatically when using the SDK, or you can implement manual refresh logic.

Below is the setup for the PureCloudPlatformClientV2 client. This object manages the authentication lifecycle.

import os
from genesyscloud.rest import Configuration
from genesyscloud.platform.client import PureCloudPlatformClientV2

def init_genesys_client():
    """
    Initializes and returns a configured Genesys Cloud platform client.
    """
    # Configuration via environment variables is recommended for security
    env_config = Configuration(
        host="https://api.mypurecloud.com",  # Replace with your region-specific URL
        client_id=os.getenv("GENESYS_CLIENT_ID"),
        client_secret=os.getenv("GENESYS_CLIENT_SECRET")
    )
    
    # The SDK handles the initial token fetch and subsequent refreshes
    # when making API calls.
    client = PureCloudPlatformClientV2(env_config)
    return client

Note on Regions: Ensure the host matches your Genesys Cloud instance region (e.g., api.us-gov-purecloud.com for US Gov, api.au-purecloud.com for Australia). Using the wrong region results in 401 Unauthorized or DNS resolution errors.

Implementation

Step 1: Identify the Conversation and Participant

Before disconnecting a participant, you must know the conversationId and the participantId. The participantId is unique to that specific leg of the conversation. If you are building a dynamic tool, you must first query the active conversation.

We will use the get_conversations_conversation_participants method to list all participants in a call.

Required Scope: conversation:participant:read

from genesyscloud.conversations.api.conversations_api import ConversationsApi
from genesyscloud.conversations.model import ParticipantWrap

def list_participants(client: PureCloudPlatformClientV2, conversation_id: str) -> list:
    """
    Retrieves all participants in a specific conversation.
    """
    api_instance = ConversationsApi(client)
    
    try:
        # Get the participants list
        response = api_instance.get_conversations_conversation_participants(
            conversation_id=conversation_id
        )
        
        # response is a ParticipantsWrap object
        if response.entities:
            participants = []
            for p in response.entities:
                participants.append({
                    "id": p.id,
                    "name": p.name,
                    "status": p.status,
                    "external_contact": p.external_contact
                })
            return participants
        else:
            return []
            
    except Exception as e:
        print(f"Error fetching participants: {e}")
        return []

Expected Response Structure:
The API returns a ParticipantsWrap object. The entities array contains Participant objects. Key fields include id (the participant ID), name (display name), and status (e.g., connected, ringing, disconnected).

Error Handling:

  • 404 Not Found: The conversation ID does not exist or has ended.
  • 403 Forbidden: The OAuth token lacks the conversation:participant:read scope.

Step 2: Execute the Disconnect Action

To disconnect a participant, you do not send a DELETE request. Instead, you send a POST request to the actions endpoint. This is a critical distinction in the Genesys Cloud API design. The action leave simulates the participant hanging up.

Endpoint: POST /api/v2/conversations/{conversationId}/participants/{participantId}/actions/participant/leave
Required Scope: conversation:participant:write

from genesyscloud.conversations.api.conversations_api import ConversationsApi
from genesyscloud.conversations.model import ParticipantActionRequest

def disconnect_participant(client: PureCloudPlatformClientV2, conversation_id: str, participant_id: str):
    """
    Disconnects a specific participant from a conversation.
    """
    api_instance = ConversationsApi(client)
    
    # The body is typically empty for the 'leave' action, 
    # but the SDK expects a ParticipantActionRequest object.
    body = ParticipantActionRequest()
    
    try:
        # Execute the leave action
        api_instance.post_conversations_conversation_participants_participant_leave(
            conversation_id=conversation_id,
            participant_id=participant_id,
            body=body
        )
        print(f"Successfully disconnected participant {participant_id} from conversation {conversation_id}.")
        return True
        
    except Exception as e:
        # Handle specific HTTP errors
        if hasattr(e, 'status') and e.status == 404:
            print(f"Participant {participant_id} not found in conversation {conversation_id}.")
        elif hasattr(e, 'status') and e.status == 409:
            print(f"Participant {participant_id} is already disconnected or the action is invalid.")
        else:
            print(f"Error disconnecting participant: {e}")
        return False

Non-Obvious Parameters:

  • The body parameter is required by the SDK method signature, even if the JSON payload sent over the wire is empty {}. You must instantiate ParticipantActionRequest().
  • The leave action is asynchronous in some contexts. The API returns 200 OK immediately, but the participant status may take a few seconds to update to disconnected in the backend.

Edge Cases:

  • Disconnecting the Last Participant: If you disconnect the last active participant, the conversation ends automatically.
  • Disconnecting a System Participant: You cannot disconnect system-generated participants (e.g., IVR flows) in the same way. Attempting to do so may result in a 400 Bad Request.
  • Already Disconnected: If the participant has already hung up, the API returns 409 Conflict.

Step 3: Verify Disconnection (Optional but Recommended)

To ensure the action succeeded, you can poll the participant status. This is useful for logging or triggering downstream events.

import time

def verify_disconnection(client: PureCloudPlatformClientV2, conversation_id: str, participant_id: str, max_retries: int = 5):
    """
    Polls the participant status to confirm disconnection.
    """
    api_instance = ConversationsApi(client)
    
    for _ in range(max_retries):
        try:
            response = api_instance.get_conversations_conversation_participant(
                conversation_id=conversation_id,
                participant_id=participant_id
            )
            
            if response.status == "disconnected":
                print("Verification: Participant is confirmed disconnected.")
                return True
            elif response.status in ["connected", "ringing", "in-progress"]:
                print(f"Waiting... Participant status is {response.status}.")
                time.sleep(2)  # Wait 2 seconds before retrying
            else:
                print(f"Unexpected status: {response.status}")
                return False
                
        except Exception as e:
            print(f"Error verifying status: {e}")
            return False
            
    print("Verification failed: Timeout waiting for disconnection confirmation.")
    return False

Complete Working Example

This script combines authentication, participant listing, and disconnection into a single runnable module. It assumes you have environment variables set for GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, and GENESYS_REGION.

import os
import sys
from genesyscloud.rest import Configuration
from genesyscloud.platform.client import PureCloudPlatformClientV2
from genesyscloud.conversations.api.conversations_api import ConversationsApi
from genesyscloud.conversations.model import ParticipantActionRequest

def init_client():
    region = os.getenv("GENESYS_REGION", "api.mypurecloud.com")
    config = Configuration(
        host=f"https://{region}",
        client_id=os.getenv("GENESYS_CLIENT_ID"),
        client_secret=os.getenv("GENESYS_CLIENT_SECRET")
    )
    return PureCloudPlatformClientV2(config)

def find_participant_by_name(client, conversation_id, target_name):
    """
    Helper to find a participant ID by their display name.
    """
    api = ConversationsApi(client)
    try:
        resp = api.get_conversations_conversation_participants(conversation_id)
        if resp.entities:
            for p in resp.entities:
                if p.name and target_name.lower() in p.name.lower():
                    return p.id
        return None
    except Exception as e:
        print(f"Error finding participant: {e}")
        return None

def main():
    # 1. Initialize Client
    print("Initializing Genesys Cloud Client...")
    client = init_client()
    
    # 2. Input Parameters
    # In a production app, these would come from a webhook payload or CLI args
    conversation_id = "YOUR_CONVERSATION_ID_HERE"
    target_participant_name = "John Doe"  # Name of the participant to disconnect
    
    if conversation_id == "YOUR_CONVERSATION_ID_HERE":
        print("Error: Please set the conversation_id in the script.")
        sys.exit(1)

    print(f"Searching for participant '{target_participant_name}' in conversation {conversation_id}...")
    
    # 3. Find Participant
    participant_id = find_participant_by_name(client, conversation_id, target_participant_name)
    
    if not participant_id:
        print(f"Participant '{target_participant_name}' not found.")
        sys.exit(1)
        
    print(f"Found participant ID: {participant_id}")

    # 4. Disconnect Participant
    print("Initiating disconnect...")
    api = ConversationsApi(client)
    body = ParticipantActionRequest()
    
    try:
        api.post_conversations_conversation_participants_participant_leave(
            conversation_id=conversation_id,
            participant_id=participant_id,
            body=body
        )
        print("Disconnect command sent successfully.")
        
    except Exception as e:
        print(f"Failed to disconnect: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 401 Unauthorized

  • Cause: The OAuth token is expired, invalid, or the client credentials are incorrect.
  • Fix: Verify that GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET are correct. Ensure the client application in the Genesys Cloud Admin Console is active and has the correct scopes assigned. If using JWT, ensure the private key matches the public key uploaded to Genesys.

Error: 403 Forbidden

  • Cause: The OAuth token does not have the required scope.
  • Fix: Check the client application scopes in Genesys Cloud. You must have conversation:participant:write. If you are only reading participants, you need conversation:participant:read.

Error: 404 Not Found

  • Cause: The conversationId or participantId does not exist.
  • Fix: Verify the conversation is still active. If the conversation ended, the participants are archived and cannot be disconnected. Use the get_conversations_conversation_participants endpoint to validate the IDs before attempting the action.

Error: 409 Conflict

  • Cause: The participant is already in a state where the action cannot be performed (e.g., already disconnected, or in the middle of another action).
  • Fix: Check the participant status via get_conversations_conversation_participant. If the status is disconnected, no action is needed. If it is connected, retry the leave action after a short delay.

Error: 429 Too Many Requests

  • Cause: You have exceeded the API rate limits.
  • Fix: Implement exponential backoff. The Genesys Cloud API returns a Retry-After header. Parse this header and wait before retrying.
import time

def safe_api_call(func, *args, **kwargs):
    """
    Wrapper to handle 429 errors with exponential backoff.
    """
    retries = 3
    for i in range(retries):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            if hasattr(e, 'status') and e.status == 429:
                retry_after = int(e.headers.get('Retry-After', 2 ** i))
                print(f"Rate limited. Retrying after {retry_after} seconds...")
                time.sleep(retry_after)
            else:
                raise

Official References