How to Mute/Unmute an Agent Microphone via the Client API During a Call
What You Will Build
- You will build a backend service that programmatically mutes or unmutes an active agent session within Genesys Cloud CX.
- This uses the Genesys Cloud CX Pure Cloud Platform Client V2 SDK and the Interactive Session API endpoints.
- The tutorial covers Python (using
httpxfor raw HTTP flexibility andgenesyscloudSDK concepts) and JavaScript (Node.js withaxios).
Prerequisites
- OAuth Client Type: A Client Credentials Grant (CCG) or Authorization Code Grant (ACG) client with administrative privileges.
- Required Scopes:
session:write,session:read. - SDK Version:
genesyscloudPython SDK v12+ or equivalent Node.js wrapper. - Runtime Requirements: Python 3.9+ or Node.js 18+.
- External Dependencies:
- Python:
httpx,pydantic(for validation),genesyscloud(optional, but recommended for token management). - JavaScript:
axios,dotenv.
- Python:
Authentication Setup
Genesys Cloud CX APIs require a valid Bearer token. For this tutorial, we assume you have a valid Client ID and Client Secret. We will implement a token fetcher to ensure the code is runnable.
OAuth Scope Requirement: To perform mute/unmute actions, the token must possess the session:write scope. If your client lacks this, the API will return a 403 Forbidden.
Python: Token Fetcher
import httpx
import os
from typing import Optional
class GenesysAuth:
def __init__(self, client_id: str, client_secret: str, org_name: str):
self.client_id = client_id
self.client_secret = client_secret
self.org_name = org_name
self.base_url = f"https://{org_name}.mygen.com/api/v2"
self.token_url = f"https://{org_name}.mygen.com/oauth/token"
self._token: Optional[str] = None
async def get_token(self) -> str:
"""Fetches a new OAuth token."""
if self._token:
return self._token
async with httpx.AsyncClient() as client:
response = await client.post(
self.token_url,
data={
"grant_type": "client_credentials",
"client_id": self.client_id,
"client_secret": self.client_secret,
"scope": "session:write session:read"
},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
response.raise_for_status()
token_data = response.json()
self._token = token_data.get("access_token")
return self._token
async def get_headers(self) -> dict:
"""Returns headers with the current Bearer token."""
token = await self.get_token()
return {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
JavaScript: Token Fetcher
const axios = require('axios');
require('dotenv').config();
class GenesysAuth {
constructor(clientId, clientSecret, orgName) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.orgName = orgName;
this.baseUrl = `https://${orgName}.mygen.com/api/v2`;
this.tokenUrl = `https://${orgName}.mygen.com/oauth/token`;
this._token = null;
}
async getHeaders() {
if (!this._token) {
await this.refreshToken();
}
return {
Authorization: `Bearer ${this._token}`,
'Content-Type': 'application/json'
};
}
async refreshToken() {
const formData = new URLSearchParams();
formData.append('grant_type', 'client_credentials');
formData.append('client_id', this.clientId);
formData.append('client_secret', this.clientSecret);
formData.append('scope', 'session:write session:read');
const response = await axios.post(this.tokenUrl, formData, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
this._token = response.data.access_token;
}
}
module.exports = GenesysAuth;
Implementation
Muting an agent is not a direct “action” on the user object. It is a state change on the Interactive Session. The Genesys Cloud architecture treats every active voice connection as a session resource. To mute an agent, you must identify the specific session associated with that agent and update its muted property.
Step 1: Identify the Active Session
You cannot mute an agent without knowing their current session ID. The session ID is unique per connection and changes when the agent logs out or ends the call.
Endpoint: GET /api/v2/flexiblesearch/queries/sessions/details/query
This endpoint allows you to filter active sessions by user ID. This is the most reliable way to find a session for a specific agent, rather than iterating through all active sessions.
Required Scope: session:read
Python Implementation
import httpx
import json
import os
from typing import Dict, Any
# Assuming GenesysAuth class is defined as above
async def find_agent_session(auth: GenesysAuth, user_id: str) -> str:
"""
Finds the active voice session for a given user ID.
Args:
auth: GenesysAuth instance
user_id: The Genesys Cloud User ID (UUID)
Returns:
session_id: The ID of the active session
"""
headers = await auth.get_headers()
endpoint = f"{auth.base_url}/flexiblesearch/queries/sessions/details/query"
# Define the query body to find active voice sessions for the user
query_body = {
"pageSize": 1,
"query": {
"type": "and",
"clauses": [
{
"type": "eq",
"field": "userId",
"values": [user_id]
},
{
"type": "eq",
"field": "type",
"values": ["voice"]
},
{
"type": "eq",
"field": "status",
"values": ["active"]
}
]
}
}
async with httpx.AsyncClient() as client:
try:
response = await client.post(
endpoint,
headers=headers,
json=query_body
)
response.raise_for_status()
data = response.json()
if not data.get("entities"):
raise ValueError(f"No active voice session found for user {user_id}")
# Extract the session ID from the first result
session_id = data["entities"][0]["id"]
return session_id
except httpx.HTTPStatusError as e:
if e.response.status_code == 401:
raise Exception("Authentication failed. Check your token scopes.")
elif e.response.status_code == 403:
raise Exception("Forbidden. Ensure the token has 'session:read' scope.")
else:
raise e
JavaScript Implementation
const axios = require('axios');
async function findAgentSession(auth, userId) {
const headers = await auth.getHeaders();
const endpoint = `${auth.baseUrl}/flexiblesearch/queries/sessions/details/query`;
const queryBody = {
pageSize: 1,
query: {
type: "and",
clauses: [
{
type: "eq",
field: "userId",
values: [userId]
},
{
type: "eq",
field: "type",
values: ["voice"]
},
{
type: "eq",
field: "status",
values: ["active"]
}
]
}
};
try {
const response = await axios.post(endpoint, queryBody, { headers });
const data = response.data;
if (!data.entities || data.entities.length === 0) {
throw new Error(`No active voice session found for user ${userId}`);
}
return data.entities[0].id;
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error("Authentication failed. Check your token scopes.");
} else if (error.response && error.response.status === 403) {
throw new Error("Forbidden. Ensure the token has 'session:read' scope.");
}
throw error;
}
}
module.exports = { findAgentSession };
Step 2: Toggle the Mute State
Once you have the session_id, you send a PATCH request to the Interactive Session endpoint. This is a partial update, meaning you only need to send the fields you wish to change.
Endpoint: PATCH /api/v2/interactive/sessions/{sessionId}
Required Scope: session:write
Critical Parameter: muted (boolean).
true: Mutes the agent (the customer cannot hear the agent).false: Unmutes the agent.
Note on Directionality: In Genesys Cloud, the muted property on a session typically refers to the agent’s audio stream being muted from the perspective of the other party. It does not mute the customer’s audio from the agent’s perspective. To mute the customer, you would need to manipulate the muted property on the customer’s session object, which requires finding the customer’s session ID first. This tutorial focuses on the agent’s microphone.
Python Implementation
async def toggle_agent_mute(auth: GenesysAuth, session_id: str, mute: bool) -> Dict[str, Any]:
"""
Mutes or unmutes the agent's microphone for a specific session.
Args:
auth: GenesysAuth instance
session_id: The ID of the interactive session
mute: True to mute, False to unmute
Returns:
Response JSON body
"""
headers = await auth.get_headers()
endpoint = f"{auth.base_url}/interactive/sessions/{session_id}"
# The PATCH body only requires the 'muted' field
payload = {
"muted": mute
}
async with httpx.AsyncClient() as client:
try:
response = await client.patch(
endpoint,
headers=headers,
json=payload
)
# 204 No Content is common for successful PATCH in Genesys,
# but sometimes it returns 200 with the updated object.
if response.status_code == 204:
return {"status": "success", "message": "Session state updated"}
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
raise Exception(f"Session {session_id} not found or ended.")
elif e.response.status_code == 403:
raise Exception("Forbidden. Ensure the token has 'session:write' scope.")
else:
raise e
JavaScript Implementation
async function toggleAgentMute(auth, sessionId, mute) {
const headers = await auth.getHeaders();
const endpoint = `${auth.baseUrl}/interactive/sessions/${sessionId}`;
const payload = {
muted: mute
};
try {
const response = await axios.patch(endpoint, payload, { headers });
if (response.status === 204) {
return { status: 'success', message: 'Session state updated' };
}
return response.data;
} catch (error) {
if (error.response && error.response.status === 404) {
throw new Error(`Session ${sessionId} not found or ended.`);
} else if (error.response && error.response.status === 403) {
throw new Error("Forbidden. Ensure the token has 'session:write' scope.");
}
throw error;
}
}
module.exports = { toggleAgentMute };
Step 3: Processing Results and Validation
After sending the PATCH request, the Genesys Cloud backend updates the session state. The change is near-instantaneous in the web client and desktop client. However, it is good practice to verify the state if your application needs to maintain strict local state consistency.
You can perform a GET request to /api/v2/interactive/sessions/{sessionId} to confirm the muted flag is now set correctly.
Python Verification Snippet
async def verify_session_state(auth: GenesysAuth, session_id: str) -> bool:
"""
Verifies the current muted state of a session.
Returns:
True if muted, False if unmuted
"""
headers = await auth.get_headers()
endpoint = f"{auth.base_url}/interactive/sessions/{session_id}"
async with httpx.AsyncClient() as client:
response = await client.get(endpoint, headers=headers)
response.raise_for_status()
data = response.json()
return data.get("muted", False)
Complete Working Example
Below is a complete, runnable Python script that ties these steps together. It assumes you have set environment variables for your Genesys Cloud credentials.
Environment Variables:
GENESYS_CLIENT_IDGENESYS_CLIENT_SECRETGENESYS_ORG_NAME(e.g.,myorg)AGENT_USER_ID(The UUID of the agent to mute)
import asyncio
import os
import httpx
from typing import Optional, Dict, Any
# --- Authentication Module ---
class GenesysAuth:
def __init__(self, client_id: str, client_secret: str, org_name: str):
self.client_id = client_id
self.client_secret = client_secret
self.org_name = org_name
self.base_url = f"https://{org_name}.mygen.com/api/v2"
self.token_url = f"https://{org_name}.mygen.com/oauth/token"
self._token: Optional[str] = None
async def get_token(self) -> str:
if self._token:
return self._token
async with httpx.AsyncClient() as client:
response = await client.post(
self.token_url,
data={
"grant_type": "client_credentials",
"client_id": self.client_id,
"client_secret": self.client_secret,
"scope": "session:write session:read"
},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
response.raise_for_status()
token_data = response.json()
self._token = token_data.get("access_token")
return self._token
async def get_headers(self) -> dict:
token = await self.get_token()
return {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# --- Logic Module ---
async def find_agent_session(auth: GenesysAuth, user_id: str) -> str:
headers = await auth.get_headers()
endpoint = f"{auth.base_url}/flexiblesearch/queries/sessions/details/query"
query_body = {
"pageSize": 1,
"query": {
"type": "and",
"clauses": [
{"type": "eq", "field": "userId", "values": [user_id]},
{"type": "eq", "field": "type", "values": ["voice"]},
{"type": "eq", "field": "status", "values": ["active"]}
]
}
}
async with httpx.AsyncClient() as client:
response = await client.post(endpoint, headers=headers, json=query_body)
response.raise_for_status()
data = response.json()
if not data.get("entities"):
raise ValueError(f"No active voice session found for user {user_id}")
return data["entities"][0]["id"]
async def toggle_agent_mute(auth: GenesysAuth, session_id: str, mute: bool) -> Dict[str, Any]:
headers = await auth.get_headers()
endpoint = f"{auth.base_url}/interactive/sessions/{session_id}"
payload = {"muted": mute}
async with httpx.AsyncClient() as client:
response = await client.patch(endpoint, headers=headers, json=payload)
response.raise_for_status()
return {"status": "success", "muted": mute}
# --- Main Execution ---
async def main():
# 1. Load Configuration
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
org_name = os.getenv("GENESYS_ORG_NAME")
agent_user_id = os.getenv("AGENT_USER_ID")
if not all([client_id, client_secret, org_name, agent_user_id]):
print("Error: Missing required environment variables.")
print("Set: GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, GENESYS_ORG_NAME, AGENT_USER_ID")
return
# 2. Initialize Auth
auth = GenesysAuth(client_id, client_secret, org_name)
try:
# 3. Find Session
print(f"Finding active session for agent {agent_user_id}...")
session_id = await find_agent_session(auth, agent_user_id)
print(f"Found session ID: {session_id}")
# 4. Mute the Agent
print("Muting agent microphone...")
result = await toggle_agent_mute(auth, session_id, mute=True)
print(f"Mute action result: {result}")
# Optional: Wait and Unmute
# await asyncio.sleep(5)
# print("Unmuting agent microphone...")
# await toggle_agent_mute(auth, session_id, mute=False)
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
asyncio.run(main())
Common Errors & Debugging
Error: 401 Unauthorized
- What causes it: The OAuth token is expired, invalid, or missing.
- How to fix it: Ensure your
GenesysAuthclass is correctly fetching a new token. Check that thegrant_typeisclient_credentialsand the client ID/secret are correct. - Code Fix: Implement token caching with expiration tracking. Tokens typically last 1 hour. Refresh the token before it expires.
Error: 403 Forbidden
- What causes it: The OAuth token does not have the required scopes.
- How to fix it: Ensure your OAuth client in the Genesys Cloud Admin Console has the
session:writeandsession:readscopes granted. - Debugging Tip: Decode the JWT token (using jwt.io) to inspect the
scopeclaim. It must includesession:write.
Error: 404 Not Found
- What causes it: The session ID is invalid, or the session has ended.
- How to fix it: Sessions are ephemeral. If the agent ends the call or logs out, the session ID becomes invalid. You must re-query for the session using
find_agent_sessionbefore attempting to mute. - Prevention: Always check for active sessions immediately before performing the mute action. Do not cache session IDs for long periods.
Error: 429 Too Many Requests
- What causes it: You have exceeded the Genesys Cloud API rate limits.
- How to fix it: Implement exponential backoff retry logic.
- Code Fix (Python):
import time
async def patch_with_retry(auth, session_id, payload, max_retries=3):
for attempt in range(max_retries):
try:
return await toggle_agent_mute(auth, session_id, payload["muted"])
except httpx.HTTPStatusError as e:
if e.response.status_code == 429 and attempt < max_retries - 1:
wait_time = 2 ** attempt # Exponential backoff: 1s, 2s, 4s
print(f"Rate limited. Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
else:
raise e
Error: Session Not Found for User
- What causes it: The user ID provided is not currently in an active voice call.
- How to fix it: Verify the user ID is correct. Check the Genesys Cloud Admin Console to see if the agent is actually logged in and in a call. The
flexiblesearchquery filters forstatus: activeandtype: voice. If the agent is in a chat or task session, this query will return empty.