How to Mute/Unmute an Agent Microphone via the Genesys Cloud API During a Call
What You Will Build
- This tutorial demonstrates how to programmatically mute and unmute the audio microphone of an active agent participant in a Genesys Cloud conversation.
- This uses the Genesys Cloud Conversations API (
/api/v2/conversations) and the Python SDKPureCloudPlatformClientV2. - The implementation is provided in Python 3.9+, utilizing the official Genesys Cloud Python SDK.
Prerequisites
- OAuth Client: A Genesys Cloud OAuth client with the following scopes:
conversations:participant:update(Required to modify participant audio settings)conversations:view(Required to locate the active conversation and participant ID)
- SDK Version:
genesys-cloud-purecloud-platform-clientversion 130.0.0 or later. - Runtime: Python 3.9 or higher.
- Dependencies:
genesys-cloud-purecloud-platform-clientpython-dotenv(for secure credential management)
Authentication Setup
Genesys Cloud uses OAuth 2.0 for authentication. For server-to-server integrations or backend services controlling agent states, the Client Credentials flow is the standard approach. This flow requires a Client ID and Client Secret, which you obtain from the Genesys Cloud Admin Console under Developers > OAuth Applications.
The Genesys Python SDK handles token acquisition and refresh automatically. You must initialize the Configuration object with your credentials and region.
import os
from dotenv import load_dotenv
from purecloud_platform_client import Configuration, PureCloudAuthStore
# Load environment variables from .env file
load_dotenv()
def create_configuration() -> Configuration:
"""
Creates and returns a configured PureCloudPlatformClientV2 Configuration object.
"""
environment = os.getenv("GENESYS_ENVIRONMENT", "mypurecloud.com")
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET must be set in environment variables.")
configuration = Configuration(
host=f"https://api.{environment}",
client_id=client_id,
client_secret=client_secret
)
# Initialize the auth store to handle token caching and refreshing
configuration.auth_store = PureCloudAuthStore(configuration)
return configuration
Implementation
Step 1: Locate the Active Conversation and Participant ID
To mute a participant, you must know two identifiers:
- The Conversation ID (
conversationId). - The Participant ID (
participantId).
The Participant ID is not the Agent’s user ID. It is a unique identifier generated for that specific session. If you already have the Conversation ID (e.g., from a webhook), you can skip to Step 2. If you need to find the conversation first, you must query the user’s active conversations.
The following code retrieves the active conversation for a specific agent and extracts the participant ID.
from purecloud_platform_client.api import ConversationApi
from purecloud_platform_client.model import ConversationParticipant
def get_active_participant_info(
configuration: Configuration,
agent_user_id: str,
conversation_type: str = "voice"
) -> tuple[str, str]:
"""
Finds the first active conversation for a user and returns the conversation ID and participant ID.
Args:
configuration: The configured PureCloud configuration object.
agent_user_id: The UUID of the agent user.
conversation_type: The type of conversation (e.g., 'voice', 'chat').
Returns:
A tuple containing (conversation_id, participant_id).
"""
api_instance = ConversationApi(configuration)
# Get active conversations for the user
# Note: This endpoint returns a list of conversations the user is currently in.
try:
response = api_instance.get_users_user_id_conversations(
user_id=agent_user_id,
conversation_type=conversation_type,
state="active"
)
if not response.entities:
raise RuntimeError(f"No active {conversation_type} conversations found for user {agent_user_id}.")
# Assume the first active conversation is the target
conversation = response.entities[0]
# Find the participant object belonging to this user within the conversation
target_participant = None
for participant in conversation.participants:
if participant.user.id == agent_user_id:
target_participant = participant
break
if not target_participant:
raise RuntimeError(f"Participant object for user {agent_user_id} not found in conversation {conversation.id}.")
return conversation.id, target_participant.id
except Exception as e:
# Handle specific HTTP errors
if hasattr(e, 'status') and e.status == 404:
raise RuntimeError(f"User {agent_user_id} not found or no active conversations.")
raise e
Step 2: Construct the Mute/Unmute Payload
The Genesys Cloud Conversations API allows you to update participant properties via a PATCH request. To mute or unmute the microphone, you must modify the media object within the participant payload.
The critical field is muted inside the audio object of the media section.
- Mute: Set
media.audio.mutedtotrue. - Unmute: Set
media.audio.mutedtofalse.
You must also provide the reason for the mute action. Genesys Cloud enforces this for audit trails. Common reasons include agent (agent initiated), supervisor (supervisor initiated), or system.
The SDK provides the ConversationParticipant model. You must instantiate this model with the media attribute populated correctly.
from purecloud_platform_client.model import (
ConversationParticipant,
ConversationParticipantMedia,
ConversationParticipantAudio
)
def create_mute_payload(participant_id: str, mute_state: bool, reason: str = "supervisor") -> ConversationParticipant:
"""
Creates the participant payload required to update the mute state.
Args:
participant_id: The UUID of the participant.
mute_state: True to mute, False to unmute.
reason: The reason for the mute (e.g., 'supervisor', 'agent').
Returns:
A ConversationParticipant object ready for the PATCH request.
"""
# Construct the Audio object
audio_config = ConversationParticipantAudio(
muted=mute_state
)
# Construct the Media object
media_config = ConversationParticipantMedia(
audio=audio_config
)
# Construct the Participant object
# Note: Only the fields you wish to update need to be provided.
# However, providing the ID is mandatory for the PATCH operation to identify the target.
participant_payload = ConversationParticipant(
id=participant_id,
media=media_config,
reason=reason
)
return participant_payload
Step 3: Execute the Mute/Unmute Command
With the payload constructed, you send it to the patch_conversations_conversation_id_participants endpoint. This endpoint accepts a list of participant updates, allowing you to update multiple participants in a single call if necessary.
Error handling is critical here. A 404 indicates the conversation or participant no longer exists (perhaps the call ended). A 400 often indicates an invalid reason code or malformed media object. A 409 may occur if the participant is already in the requested state or if the call is in a state that does not allow audio modification (e.g., disconnected).
from purecloud_platform_client.api import ConversationApi
from purecloud_platform_client.rest import ApiException
def update_participant_mute_state(
configuration: Configuration,
conversation_id: str,
participant_id: str,
mute: bool,
reason: str = "supervisor"
) -> bool:
"""
Mutes or unmutes a participant in an active conversation.
Args:
configuration: The configured PureCloud configuration object.
conversation_id: The UUID of the conversation.
participant_id: The UUID of the participant.
mute: True to mute, False to unmute.
reason: The reason for the mute action.
Returns:
True if the operation was successful.
Raises:
ApiException: If the API call fails.
"""
api_instance = ConversationApi(configuration)
# Create the payload
participant_payload = create_mute_payload(participant_id, mute, reason)
# The API expects a list of participants
participants_list = [participant_payload]
try:
# Execute the PATCH request
api_instance.patch_conversations_conversation_id_participants(
conversation_id=conversation_id,
body=participants_list
)
print(f"Participant {participant_id} {'muted' if mute else 'unmuted'} successfully in conversation {conversation_id}.")
return True
except ApiException as e:
print(f"Exception when calling ConversationApi->patch_conversations_conversation_id_participants: {e}")
# Handle specific error codes
if e.status == 404:
raise RuntimeError(f"Conversation {conversation_id} or Participant {participant_id} not found. The call may have ended.")
elif e.status == 400:
raise RuntimeError(f"Bad Request. Check the 'reason' field and mute state. Reason: {e.reason}")
elif e.status == 409:
raise RuntimeError(f"Conflict. The participant may already be in the requested state or the call is not active.")
else:
raise e
Complete Working Example
This script combines all steps into a single executable module. It assumes you have a .env file with GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, GENESYS_ENVIRONMENT, and AGENT_USER_ID.
import os
import sys
from dotenv import load_dotenv
from purecloud_platform_client import Configuration, PureCloudAuthStore
from purecloud_platform_client.api import ConversationApi
from purecloud_platform_client.model import (
ConversationParticipant,
ConversationParticipantMedia,
ConversationParticipantAudio
)
from purecloud_platform_client.rest import ApiException
# Load environment variables
load_dotenv()
def create_configuration() -> Configuration:
environment = os.getenv("GENESYS_ENVIRONMENT", "mypurecloud.com")
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET must be set in environment variables.")
configuration = Configuration(
host=f"https://api.{environment}",
client_id=client_id,
client_secret=client_secret
)
configuration.auth_store = PureCloudAuthStore(configuration)
return configuration
def get_active_participant_info(configuration: Configuration, agent_user_id: str) -> tuple[str, str]:
api_instance = ConversationApi(configuration)
try:
response = api_instance.get_users_user_id_conversations(
user_id=agent_user_id,
conversation_type="voice",
state="active"
)
if not response.entities:
raise RuntimeError(f"No active voice conversations found for user {agent_user_id}.")
conversation = response.entities[0]
target_participant = None
for participant in conversation.participants:
if participant.user.id == agent_user_id:
target_participant = participant
break
if not target_participant:
raise RuntimeError(f"Participant object for user {agent_user_id} not found in conversation {conversation.id}.")
return conversation.id, target_participant.id
except ApiException as e:
raise RuntimeError(f"Failed to get active conversations: {e}")
def create_mute_payload(participant_id: str, mute_state: bool, reason: str = "supervisor") -> ConversationParticipant:
audio_config = ConversationParticipantAudio(muted=mute_state)
media_config = ConversationParticipantMedia(audio=audio_config)
return ConversationParticipant(id=participant_id, media=media_config, reason=reason)
def update_participant_mute_state(configuration: Configuration, conversation_id: str, participant_id: str, mute: bool, reason: str = "supervisor") -> bool:
api_instance = ConversationApi(configuration)
participant_payload = create_mute_payload(participant_id, mute, reason)
participants_list = [participant_payload]
try:
api_instance.patch_conversations_conversation_id_participants(
conversation_id=conversation_id,
body=participants_list
)
return True
except ApiException as e:
if e.status == 404:
raise RuntimeError(f"Conversation or Participant not found. Call may have ended.")
elif e.status == 400:
raise RuntimeError(f"Bad Request: {e.body}")
elif e.status == 409:
raise RuntimeError(f"Conflict: Participant state conflict.")
raise e
def main():
# Configuration
config = create_configuration()
agent_user_id = os.getenv("AGENT_USER_ID")
if not agent_user_id:
print("Error: AGENT_USER_ID is not set in environment variables.")
sys.exit(1)
try:
# Step 1: Find the active call
print(f"Searching for active voice conversations for user {agent_user_id}...")
conversation_id, participant_id = get_active_participant_info(config, agent_user_id)
print(f"Found active conversation: {conversation_id}")
print(f"Participant ID: {participant_id}")
# Step 2: Mute the agent
print("Muting agent microphone...")
update_participant_mute_state(config, conversation_id, participant_id, mute=True, reason="supervisor")
# Step 3: Unmute the agent (for demonstration)
print("Unmuting agent microphone...")
update_participant_mute_state(config, conversation_id, participant_id, mute=False, reason="supervisor")
print("Operation completed successfully.")
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden
- Cause: The OAuth client does not have the
conversations:participant:updatescope. - Fix: Go to the Genesys Cloud Admin Console, navigate to Developers > OAuth Applications, select your client, and ensure the
conversations:participant:updatescope is checked. Regenerate the token or restart your application to pick up the new permissions.
Error: 404 Not Found
- Cause: The
conversationIdorparticipantIdis invalid or the conversation has ended. - Fix: Verify the IDs. If you are using IDs from a previous webhook, ensure the call is still active. Participant IDs are ephemeral and only valid for the duration of the conversation.
Error: 400 Bad Request
- Cause: The
reasonfield is missing or invalid, or themediaobject is malformed. - Fix: Ensure the
reasonfield is one of the accepted values (agent,supervisor,system,customer). Ensure themedia.audio.mutedfield is a boolean, not a string.
Error: 409 Conflict
- Cause: The participant is already in the requested mute state, or the call is in a terminal state (e.g.,
disconnected). - Fix: Check the current state of the participant before sending the update. If the call has ended, the operation is irrelevant.