Disconnect a Specific Participant from a Conference Call via Genesys Cloud Conversations API

Disconnect a Specific Participant from a Conference Call via Genesys Cloud Conversations API

What You Will Build

  • This tutorial demonstrates how to programmatically disconnect a specific participant from an active conference call using the Genesys Cloud CX Conversations API.
  • You will use the POST /api/v2/conversations/{conversationId}/participants/{participantId}/disconnect endpoint to terminate a participant’s session.
  • The implementation covers Python and JavaScript, handling authentication, participant identification, and error recovery.

Prerequisites

  • OAuth Client: A Genesys Cloud OAuth Client ID and Client Secret with the public grant type.
  • Required Scopes: conversation:participant:write is mandatory to modify participant state.
  • SDK Versions:
    • Python: genesys-cloud-sdk v12.0.0+
    • JavaScript: @genesyscloud/genesys-cloud-sdk v4.0.0+
  • Runtime:
    • Python 3.9+
    • Node.js 18+
  • Dependencies:
    • Python: pip install genesys-cloud-sdk
    • JavaScript: npm install @genesyscloud/genesys-cloud-sdk

Authentication Setup

Genesys Cloud uses OAuth 2.0 for API authentication. You must obtain a valid access token before making API calls. The tokens expire after 3600 seconds (1 hour). In production, you should cache tokens and refresh them before expiration.

For this tutorial, we will use the Client Credentials Grant flow, which is standard for server-to-server integrations.

Python Authentication

import time
from purecloud_platform_client_v2 import Configuration, OAuthClient, ApiClient

def get_access_token(client_id: str, client_secret: str, base_url: str = "https://api.mypurecloud.com") -> str:
    """
    Retrieves an OAuth access token using Client Credentials Grant.
    """
    oauth_client = OAuthClient(
        client_id=client_id,
        client_secret=client_secret,
        base_url=base_url
    )
    
    try:
        # Request token with required scope
        oauth_client.get_token(scope="conversation:participant:write")
        return oauth_client.access_token
    except Exception as e:
        raise RuntimeError(f"Failed to obtain access token: {e}")

JavaScript Authentication

import { PlatformClient } from '@genesyscloud/genesys-cloud-sdk';

/**
 * Initializes the platform client and authenticates.
 * Note: The JS SDK handles token refresh automatically if configured correctly.
 */
let platformClient;

async function initPlatformClient(clientId, clientSecret, region = 'us-east-1') {
    const baseUri = `https://api.${region}.mypurecloud.com`;
    
    platformClient = new PlatformClient({
        baseUri: baseUri,
        clientId: clientId,
        clientSecret: clientSecret
    });

    // Authenticate once at startup
    await platformClient.auth.login({
        grantType: 'client_credentials',
        scope: ['conversation:participant:write']
    });

    return platformClient;
}

Implementation

Step 1: Identify the Conversation and Participant

Before disconnecting a participant, you must know the conversationId and the specific participantId. The participantId is a UUID generated when the participant joins the conversation.

If you do not have the participantId, you must query the conversation details first.

Fetching Conversation Details (Python)

from purecloud_platform_client_v2 import ConversationsApi, ApiClient, Configuration

def get_participants(conversation_id: str, access_token: str) -> list:
    """
    Retrieves all participants in a specific conversation.
    Returns a list of participant objects.
    """
    configuration = Configuration(access_token=access_token)
    api_client = ApiClient(configuration)
    conversations_api = ConversationsApi(api_client)

    try:
        # Get the full conversation object including participants
        response = conversations_api.get_conversation(conversation_id)
        
        if not response.participants:
            return []
            
        return response.participants
    except Exception as e:
        # Handle 404 (Conversation not found) or 401 (Unauthorized)
        print(f"Error fetching participants: {e}")
        return []

Fetching Conversation Details (JavaScript)

async function getParticipants(conversationId) {
    try {
        // The SDK method returns a Promise resolving to the Conversation object
        const conversation = await platformClient.conversations.getConversation({
            conversationId: conversationId
        });

        if (!conversation.participants || conversation.participants.length === 0) {
            return [];
        }

        return conversation.participants;
    } catch (error) {
        console.error(`Failed to fetch participants for conversation ${conversationId}:`, error);
        throw error;
    }
}

Step 2: Disconnect the Participant

The core action uses the POST /api/v2/conversations/{conversationId}/participants/{participantId}/disconnect endpoint.

Important Notes:

  • The HTTP method is POST, not DELETE. This is a common point of confusion.
  • The request body is optional. You can pass a DisconnectParticipantRequest object to specify a reason code or message, but it is not required for a basic disconnect.
  • If the participant is already disconnected, the API returns a 204 No Content or 404 Not Found depending on the state.

Python Implementation

from purecloud_platform_client_v2 import ConversationsApi, ApiClient, Configuration
from purecloud_platform_client_v2.models import DisconnectParticipantRequest

def disconnect_participant(conversation_id: str, participant_id: str, access_token: str, reason: str = None) -> bool:
    """
    Disconnects a specific participant from a conversation.
    
    Args:
        conversation_id: The UUID of the conversation.
        participant_id: The UUID of the participant to disconnect.
        access_token: Valid OAuth access token.
        reason: Optional reason for disconnect (e.g., 'agent_initiated').
        
    Returns:
        True if successful, False otherwise.
    """
    configuration = Configuration(access_token=access_token)
    api_client = ApiClient(configuration)
    conversations_api = ConversationsApi(api_client)

    try:
        # Optional: Construct a body with a reason code
        body = None
        if reason:
            body = DisconnectParticipantRequest(reason=reason)

        # Execute the disconnect
        # This endpoint returns 204 No Content on success
        conversations_api.post_conversation_participant_disconnect(
            conversation_id=conversation_id,
            participant_id=participant_id,
            body=body
        )
        
        return True

    except Exception as e:
        # Check for specific status codes
        if hasattr(e, 'status') and e.status == 404:
            print(f"Participant {participant_id} not found or already disconnected.")
        elif hasattr(e, 'status') and e.status == 403:
            print("Insufficient permissions. Ensure 'conversation:participant:write' scope is granted.")
        else:
            print(f"Error disconnecting participant: {e}")
        return False

JavaScript Implementation

/**
 * Disconnects a specific participant from a conversation.
 */
async function disconnectParticipant(conversationId, participantId, reason = null) {
    try {
        // Construct the body if a reason is provided
        const body = reason ? { reason: reason } : undefined;

        // Call the API
        // The JS SDK method corresponds to POST /api/v2/conversations/{conversationId}/participants/{participantId}/disconnect
        await platformClient.conversations.postConversationParticipantDisconnect({
            conversationId: conversationId,
            participantId: participantId,
            body: body
        });

        console.log(`Successfully disconnected participant ${participantId}`);
        return true;

    } catch (error) {
        if (error.status === 404) {
            console.warn(`Participant ${participantId} not found in conversation ${conversationId}.`);
        } else if (error.status === 403) {
            console.error("Forbidden: Check OAuth scopes.");
        } else {
            console.error(`Failed to disconnect participant:`, error);
        }
        return false;
    }
}

Step 3: Processing Results and Edge Cases

When disconnecting a participant, several edge cases must be handled:

  1. Participant Already Disconnected: If the participant has already left the call, the API may return a 404. This is not an error in business logic; it means the goal is already achieved.
  2. Conversation Ended: If the entire conversation has ended, the API returns a 404.
  3. Rate Limiting (429): Genesys Cloud enforces rate limits. If you receive a 429, you must wait before retrying. The Retry-After header indicates the wait time in seconds.

Handling Rate Limits in Python

import time

def disconnect_with_retry(conversation_id: str, participant_id: str, access_token: str, max_retries: int = 3) -> bool:
    """
    Disconnects a participant with exponential backoff for 429 errors.
    """
    configuration = Configuration(access_token=access_token)
    api_client = ApiClient(configuration)
    conversations_api = ConversationsApi(api_client)

    for attempt in range(max_retries):
        try:
            conversations_api.post_conversation_participant_disconnect(
                conversation_id=conversation_id,
                participant_id=participant_id
            )
            return True
            
        except Exception as e:
            if hasattr(e, 'status') and e.status == 429:
                wait_time = int(e.headers.get('Retry-After', 2 ** attempt))
                print(f"Rate limited. Waiting {wait_time} seconds before retry...")
                time.sleep(wait_time)
            else:
                # Non-retryable error
                print(f"Non-retryable error: {e}")
                return False
                
    return False

Complete Working Example

This Python script combines authentication, participant lookup, and disconnection into a single workflow.

import sys
import time
from purecloud_platform_client_v2 import Configuration, OAuthClient, ApiClient, ConversationsApi
from purecloud_platform_client_v2.models import DisconnectParticipantRequest

# Configuration
CLIENT_ID = "YOUR_CLIENT_ID"
CLIENT_SECRET = "YOUR_CLIENT_SECRET"
BASE_URL = "https://api.mypurecloud.com"
CONVERSATION_ID = "YOUR_CONVERSATION_UUID"
PARTICIPANT_ID = "YOUR_PARTICIPANT_UUID" # Optional: If not provided, script will list participants

def main():
    # 1. Authenticate
    print("Authenticating...")
    oauth_client = OAuthClient(
        client_id=CLIENT_ID,
        client_secret=CLIENT_SECRET,
        base_url=BASE_URL
    )
    
    try:
        oauth_client.get_token(scope="conversation:participant:write")
        access_token = oauth_client.access_token
        print("Authentication successful.")
    except Exception as e:
        print(f"Authentication failed: {e}")
        sys.exit(1)

    # 2. Initialize API Client
    configuration = Configuration(access_token=access_token)
    api_client = ApiClient(configuration)
    conversations_api = ConversationsApi(api_client)

    # 3. Identify Participant (if not provided)
    if not PARTICIPANT_ID:
        print(f"Fetching participants for conversation {CONVERSATION_ID}...")
        try:
            conv = conversations_api.get_conversation(CONVERSATION_ID)
            if not conv.participants:
                print("No participants found in this conversation.")
                sys.exit(0)
            
            print("Available Participants:")
            for p in conv.participants:
                print(f"  ID: {p.id}, Name: {p.name or 'Unknown'}, Type: {p.type}")
            
            # For this example, we assume we want to disconnect the first participant
            # In a real app, you would prompt the user or use a specific identifier
            target_participant = conv.participants[0]
            PARTICIPANT_ID = target_participant.id
            print(f"Selected participant to disconnect: {PARTICIPANT_ID}")
        except Exception as e:
            print(f"Error fetching conversation: {e}")
            sys.exit(1)

    # 4. Disconnect Participant
    print(f"Disconnecting participant {PARTICIPANT_ID} from conversation {CONVERSATION_ID}...")
    
    try:
        # Optional: Add a reason
        body = DisconnectParticipantRequest(reason="administrative_disconnect")
        
        conversations_api.post_conversation_participant_disconnect(
            conversation_id=CONVERSATION_ID,
            participant_id=PARTICIPANT_ID,
            body=body
        )
        print("Participant disconnected successfully.")
        
    except Exception as e:
        if hasattr(e, 'status'):
            if e.status == 404:
                print("Participant not found or already disconnected.")
            elif e.status == 403:
                print("Permission denied. Check OAuth scopes.")
            elif e.status == 429:
                print("Rate limit exceeded. Try again later.")
            else:
                print(f"API Error {e.status}: {e.reason}")
        else:
            print(f"Unexpected error: {e}")

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 401 Unauthorized

  • Cause: The access token is expired, invalid, or missing.
  • Fix: Ensure you are calling oauth_client.get_token() before making API requests. Check that your client ID and secret are correct. Verify that the scope includes conversation:participant:write.

Error: 403 Forbidden

  • Cause: The OAuth client does not have the required permissions.
  • Fix: Go to the Genesys Cloud Admin Console > Admin > Security > OAuth. Select your client and ensure conversation:participant:write is checked under Scopes. Save the changes. Note: You may need to regenerate the token after changing scopes.

Error: 404 Not Found

  • Cause: The conversationId or participantId is incorrect, or the participant has already disconnected.
  • Fix: Verify the UUIDs. Use the GET /api/v2/conversations/{conversationId} endpoint to confirm the participant exists in the participants array. If the participant is already gone, treat this as a success condition in your logic.

Error: 429 Too Many Requests

  • Cause: You have exceeded the API rate limit for your organization or client.
  • Fix: Implement exponential backoff. Read the Retry-After header from the response to determine how long to wait. Do not retry immediately.

Error: 500 Internal Server Error

  • Cause: A transient issue on the Genesys Cloud platform.
  • Fix: Retry the request after a short delay (e.g., 1-2 seconds). If the error persists, check the Genesys Cloud status page for outages.

Official References