How to Control Agent Microphone Mute State via Genesys Cloud APIs
What You Will Build
- A backend service that programmatically mutes or unmutes an agent’s microphone during an active voice conversation.
- Implementation uses the Genesys Cloud Platform Client SDK (Python) and direct REST API calls.
- The tutorial covers Python and JavaScript, demonstrating how to locate active conversations and execute the mute/unmute action.
Prerequisites
- OAuth Client Type: Application-to-Application (JWT) or Confidential Client (Client Credentials). Public clients cannot perform these administrative actions.
- Required Scopes:
conversation:write(To modify conversation properties)conversation:view(To list and query conversations)user:view(If searching by user ID)
- SDK Version: Genesys Cloud Python SDK
>= 145.0.0or JavaScript SDK>= 145.0.0. - Runtime Requirements: Python 3.8+ or Node.js 16+.
- External Dependencies:
- Python:
genesyscloud,requests(for raw API fallback) - JavaScript:
@genesyscloud/platform-client-v2,axios
- Python:
Authentication Setup
Before making any API calls, you must obtain a valid access token. The mute/unmute action requires an authenticated session with write permissions on conversations.
Python (SDK Authentication)
from purecloudplatformclient_v2 import Configuration, ApiClient, PureCloudAuthenticator
def get_api_client(client_id: str, client_secret: str, env_url: str = "https://api.mypurecloud.com"):
"""
Initializes the Genesys Cloud API client using Client Credentials flow.
Args:
client_id: OAuth Client ID
client_secret: OAuth Client Secret
env_url: Base URL for the environment (US, EU, etc.)
Returns:
ApiClient: Configured API client instance
"""
try:
# Create the configuration object
config = Configuration()
config.host = env_url
# Create the authenticator
authenticator = PureCloudAuthenticator(client_id, client_secret)
config.authenticator = authenticator
# Create the API client
api_client = ApiClient(configuration=config)
# Verify connectivity by fetching a dummy resource or just returning
# The SDK handles token refresh automatically upon first API call
return api_client
except Exception as e:
print(f"Authentication failed: {e}")
raise
JavaScript (SDK Authentication)
const PlatformClient = require('@genesyscloud/platform-client-v2');
const initPlatformClient = (clientId, clientSecret, envUrl = 'https://api.mypurecloud.com') => {
return PlatformClient.init({
clientId: clientId,
clientSecret: clientSecret,
environment: PlatformClient.Environment.PROD, // Adjust for EU/JP if necessary
baseUrl: envUrl
}).then(() => {
return PlatformClient;
}).catch(err => {
console.error("Platform Client Initialization Error:", err);
throw err;
});
};
Implementation
Step 1: Identify the Active Conversation
You cannot mute an agent without knowing the specific conversationId and the participantId of the agent. The agent may be in multiple concurrent conversations. You must filter for active voice conversations associated with a specific user.
Python: Querying Active Conversations
The ConversationsApi allows you to query for live conversations. We will filter by type: voice and status: active.
from purecloudplatformclient_v2 import ConversationsApi, ConversationSearchQuery
def find_active_voice_conversation(api_client, user_id: str):
"""
Finds an active voice conversation for a specific user.
Args:
api_client: The initialized ApiClient
user_id: The ID of the agent user
Returns:
dict: The conversation object if found, None otherwise
"""
conversations_api = ConversationsApi(api_client)
# Define the search query
# Note: The SDK constructs the JSON body automatically
query_body = ConversationSearchQuery(
filters=[
{
"type": "conversation",
"field": "type",
"operator": "in",
"values": ["voice"]
},
{
"type": "conversation",
"field": "status",
"operator": "eq",
"values": ["active"]
},
{
"type": "participant",
"field": "userId",
"operator": "eq",
"values": [user_id]
}
]
)
try:
# Execute the search
# max_results=1 is sufficient if we expect only one primary call
result = conversations_api.post_conversations_search(query_body=query_body, max_results=1)
if result.conversations and len(result.conversations) > 0:
return result.conversations[0]
else:
print(f"No active voice conversations found for user {user_id}")
return None
except Exception as e:
print(f"Error querying conversations: {e}")
raise
JavaScript: Querying Active Conversations
const findActiveVoiceConversation = async (platformClient, userId) => {
const conversationsApi = platformClient.ConversationsApi;
const queryBody = {
filters: [
{
type: "conversation",
field: "type",
operator: "in",
values: ["voice"]
},
{
type: "conversation",
field: "status",
operator: "eq",
values: ["active"]
},
{
type: "participant",
field: "userId",
operator: "eq",
values: [userId]
}
]
};
try {
const response = await conversationsApi.postConversationsSearch(queryBody, { maxResults: 1 });
if (response.conversations && response.conversations.length > 0) {
return response.conversations[0];
} else {
console.log(`No active voice conversations found for user ${userId}`);
return null;
}
} catch (error) {
console.error("Error querying conversations:", error);
throw error;
}
};
Step 2: Execute the Mute/Unmute Action
The mute state is controlled via the PATCH method on the participant endpoint. You must update the muted property within the participant object.
Critical API Detail:
The endpoint is PATCH /api/v2/conversations/{conversationId}/participants/{participantId}.
The request body must contain the muted field set to true (mute) or false (unmute).
Scope Required: conversation:write.
Python: Toggling Mute State
from purecloudplatformclient_v2 import ParticipantUpdateRequest
def toggle_agent_mute(api_client, conversation_id: str, participant_id: str, mute: bool):
"""
Mutes or unmutes a specific participant in a conversation.
Args:
api_client: The initialized ApiClient
conversation_id: The ID of the conversation
participant_id: The ID of the participant (agent)
mute: Boolean - True to mute, False to unmute
Returns:
dict: The updated participant object
"""
conversations_api = ConversationsApi(api_client)
# Create the update request body
# Only the 'muted' field is required for this operation
update_body = ParticipantUpdateRequest(muted=mute)
try:
# Perform the PATCH operation
# The SDK maps this to PATCH /api/v2/conversations/{conversationId}/participants/{participantId}
response = conversations_api.patch_conversation_participant(
conversation_id=conversation_id,
participant_id=participant_id,
body=update_body
)
print(f"Successfully {'muted' if mute else 'unmuted'} participant {participant_id}")
return response
except Exception as e:
# Handle specific HTTP errors
if hasattr(e, 'status') and e.status == 404:
print(f"Conversation or Participant not found. ID: {conversation_id}/{participant_id}")
elif hasattr(e, 'status') and e.status == 403:
print("Forbidden: Check if your OAuth token has 'conversation:write' scope.")
else:
print(f"Error toggling mute: {e}")
raise
JavaScript: Toggling Mute State
const toggleAgentMute = async (platformClient, conversationId, participantId, mute) => {
const conversationsApi = platformClient.ConversationsApi;
const updateBody = {
muted: mute
};
try {
// Perform the PATCH operation
const response = await conversationsApi.patchConversationParticipant(
conversationId,
participantId,
updateBody
);
console.log(`Successfully ${mute ? 'muted' : 'unmuted'} participant ${participantId}`);
return response;
} catch (error) {
if (error.status === 404) {
console.error(`Conversation or Participant not found. ID: ${conversationId}/${participantId}`);
} else if (error.status === 403) {
console.error("Forbidden: Check if your OAuth token has 'conversation:write' scope.");
} else {
console.error("Error toggling mute:", error);
}
throw error;
}
};
Step 3: Processing Results and Verification
After the PATCH request, the API returns the updated participant object. You should verify the muted field in the response to ensure the state change was applied.
Expected Response Body (JSON):
{
"id": "12345678-1234-1234-1234-123456789012",
"externalId": null,
"userId": "87654321-4321-4321-4321-210987654321",
"userRoutingData": {
"address": "sip:agent@example.com"
},
"mediaStates": [
{
"mediaType": "audio",
"active": true,
"muted": true,
"direction": "outbound",
"lastActivityTimestamp": "2023-10-27T10:00:00.000Z"
}
],
"muted": true,
"hold": false,
"wrapUp": false,
"participantType": "agent",
"conversationId": "11111111-1111-1111-1111-111111111111"
}
Note that muted is present at the root level of the participant object. Additionally, the mediaStates array contains the granular state per media type (audio/video). The root muted field is the primary control for voice calls.
Complete Working Example
Below is a complete, runnable Python script that combines authentication, conversation lookup, and mute toggling.
File: agent_mute_controller.py
import sys
import os
from purecloudplatformclient_v2 import (
Configuration,
ApiClient,
PureCloudAuthenticator,
ConversationsApi,
ConversationSearchQuery,
ParticipantUpdateRequest
)
# Configuration Constants
CLIENT_ID = os.getenv('GENESYS_CLIENT_ID')
CLIENT_SECRET = os.getenv('GENESYS_CLIENT_SECRET')
ENV_URL = os.getenv('GENESYS_ENV_URL', 'https://api.mypurecloud.com')
AGENT_USER_ID = os.getenv('AGENT_USER_ID') # The User ID of the agent to mute
def main():
if not CLIENT_ID or not CLIENT_SECRET or not AGENT_USER_ID:
print("Error: Missing environment variables. Set GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, and AGENT_USER_ID.")
sys.exit(1)
# 1. Initialize API Client
try:
config = Configuration()
config.host = ENV_URL
authenticator = PureCloudAuthenticator(CLIENT_ID, CLIENT_SECRET)
config.authenticator = authenticator
api_client = ApiClient(configuration=config)
print("API Client initialized successfully.")
except Exception as e:
print(f"Failed to initialize API Client: {e}")
sys.exit(1)
conversations_api = ConversationsApi(api_client)
# 2. Find Active Conversation
try:
query_body = ConversationSearchQuery(
filters=[
{"type": "conversation", "field": "type", "operator": "in", "values": ["voice"]},
{"type": "conversation", "field": "status", "operator": "eq", "values": ["active"]},
{"type": "participant", "field": "userId", "operator": "eq", "values": [AGENT_USER_ID]}
]
)
print(f"Searching for active voice conversations for user: {AGENT_USER_ID}")
result = conversations_api.post_conversations_search(query_body=query_body, max_results=1)
if not result.conversations or len(result.conversations) == 0:
print("No active voice conversations found for this agent.")
return
conversation = result.conversations[0]
conversation_id = conversation.id
print(f"Found conversation: {conversation_id}")
# Identify the agent participant
agent_participant_id = None
for participant in conversation.participants:
if participant.user_id == AGENT_USER_ID:
agent_participant_id = participant.id
break
if not agent_participant_id:
print("Error: Agent participant not found in conversation.")
return
print(f"Agent Participant ID: {agent_participant_id}")
except Exception as e:
print(f"Error searching conversations: {e}")
sys.exit(1)
# 3. Toggle Mute (Example: Mute the agent)
try:
# Set to True to MUTE, False to UNMUTE
mute_state = True
print(f"Toggling mute state to: {mute_state}")
update_body = ParticipantUpdateRequest(muted=mute_state)
response = conversations_api.patch_conversation_participant(
conversation_id=conversation_id,
participant_id=agent_participant_id,
body=update_body
)
print(f"Success! Agent is now {'MUTED' if response.muted else 'UNMUTED'}.")
except Exception as e:
print(f"Error toggling mute: {e}")
if hasattr(e, 'body'):
print(f"API Response Body: {e.body}")
sys.exit(1)
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden
Cause: The OAuth token used does not have the conversation:write scope.
Fix:
- Check your OAuth Client configuration in the Genesys Cloud Admin Console.
- Ensure the
conversation:writescope is checked under the “Scopes” section. - Regenerate the token or refresh the client credentials.
Code Check:
# Verify scope in token payload if using JWT
# Scope must include: conversation:write
Error: 404 Not Found
Cause: The conversationId or participantId is invalid or the conversation has ended.
Fix:
- Ensure the conversation is still
active. If the call ended, the API returns 404. - Verify the
participantIdmatches the agent, not the customer. - Check for typos in the IDs.
Code Check:
# Always validate participant ID before PATCH
if participant.user_id != AGENT_USER_ID:
raise ValueError("Participant is not the target agent")
Error: 429 Too Many Requests
Cause: You are exceeding the rate limit for the API endpoint.
Fix:
- Implement exponential backoff in your retry logic.
- Do not poll the mute state rapidly. Mute actions are event-driven.
Retry Logic Example (Python):
import time
def patch_with_retry(api_call, max_retries=3):
for attempt in range(max_retries):
try:
return api_call()
except Exception as e:
if hasattr(e, 'status') and e.status == 429:
wait_time = 2 ** attempt
print(f"Rate limited. Waiting {wait_time} seconds...")
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")
Error: 400 Bad Request
Cause: The request body is malformed or missing required fields.
Fix:
- Ensure the
ParticipantUpdateRequestobject contains themutedboolean. - Do not send null values for optional fields unless explicitly supported.
Code Check:
# Correct
update_body = ParticipantUpdateRequest(muted=True)
# Incorrect (Missing muted field)
update_body = ParticipantUpdateRequest()