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
genesyscloudSDK.
Prerequisites
- OAuth Client Type: Machine-to-Machine (JWT) or Client Credentials.
- Required Scopes:
conversation:participant:writeis mandatory to execute participant actions.conversation:readmay be needed if you are dynamically discovering participant IDs. - SDK Version:
genesyscloudPython 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 theconversation:participant:readscope.
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
bodyparameter is required by the SDK method signature, even if the JSON payload sent over the wire is empty{}. You must instantiateParticipantActionRequest(). - The
leaveaction is asynchronous in some contexts. The API returns200 OKimmediately, but the participant status may take a few seconds to update todisconnectedin 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_IDandGENESYS_CLIENT_SECRETare 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 needconversation:participant:read.
Error: 404 Not Found
- Cause: The
conversationIdorparticipantIddoes 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_participantsendpoint 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 isdisconnected, no action is needed. If it isconnected, 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-Afterheader. 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