Setting participant attributes mid-conversation via the Conversations API
What You Will Build
- One sentence: This tutorial demonstrates how to programmatically update participant attributes (such as
customer_idorqueue_priority) while a conversation is actively running. - One sentence: This uses the Genesys Cloud CX Conversations API (
/api/v2/conversations/...) and the Python SDK. - One sentence: The programming language covered is Python, using the
genesyscloudSDK.
Prerequisites
- OAuth client type: Confidential Client (Client Credentials Grant) or Public Client (PKCE). For server-side mid-conversation updates, Confidential Client is standard.
- Required Scopes:
conversation:participant:write(Required to modify participant data)conversation:read(Required to fetch current conversation state if needed)
- SDK Version:
genesyscloudPython SDK v5.0.0+ (compatible with Genesys Cloud API v2). - Language/Runtime: Python 3.8+.
- External Dependencies:
genesyscloud: The official Genesys Cloud CX Python SDK.python-dotenv: For secure credential management.
Install the dependencies:
pip install genesyscloud python-dotenv
Authentication Setup
Genesys Cloud uses OAuth 2.0 for all API access. For server-to-server integrations, the Client Credentials flow is the most robust. You must obtain an access token before making any requests. The SDK handles the token lifecycle, but you must initialize the client correctly.
Create a .env file in your project root:
GENESYS_CLIENT_ID=your_client_id
GENESYS_CLIENT_SECRET=your_client_secret
GENESYS_REGION=us-east-1
Initialize the client in your script:
import os
import logging
from genesyscloud import Configuration, ApiClient, ConversationsApi
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Configure the client
configuration = Configuration()
configuration.host = f"https://api.{os.getenv('GENESYS_REGION')}.mypurecloud.com"
configuration.access_token = None # Will be set by the auth helper
# Create the API client
api_client = ApiClient(configuration)
# Authenticate using Client Credentials
# Note: In production, wrap this in a try/except block
try:
api_client.login(
client_id=os.getenv('GENESYS_CLIENT_ID'),
client_secret=os.getenv('GENESYS_CLIENT_SECRET')
)
print("Authentication successful.")
except Exception as e:
logging.error(f"Authentication failed: {e}")
raise SystemExit("Could not authenticate with Genesys Cloud.")
# Initialize the Conversations API client
conversations_api = ConversationsApi(api_client)
Critical Note on Scope: If your OAuth application does not have the conversation:participant:write scope, the API call will return a 403 Forbidden error. Ensure the scope is added in the Genesys Cloud Admin Console under Organization > OAuth 2.0 > Applications.
Implementation
Step 1: Identify the Conversation and Participant
Before updating attributes, you must know the conversation_id and the participant_id. In a typical mid-conversation workflow, these are often available in the event stream (WebSockets) or passed via your integration webhook.
If you do not have these IDs, you can query for them. However, querying by external_contact_id is often the most reliable method if you have mapped the customer to an external ID.
def find_conversation_participants(external_contact_id: str, conversation_type: str = "voice"):
"""
Finds active conversations for a specific external contact ID.
"""
# Construct the query body
# We filter by type and status to ensure we only get active conversations
query_body = {
"types": [conversation_type],
"statuses": ["active", "queued"],
"externalContactIds": [external_contact_id]
}
try:
# Use the ConversationsSearch API if available, or fall back to listing
# For simplicity in this tutorial, we assume you have the conversation_id
# and participant_id from your application context (e.g., from a webhook payload).
pass
except Exception as e:
logging.error(f"Error finding conversation: {e}")
raise
In a real production scenario, you likely receive the conversation_id and participant_id in the payload of a conversation:participant:updated or conversation:created event. For this tutorial, we will assume these variables are available:
CONVERSATION_ID = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
PARTICIPANT_ID = "user-12345678-90ab-cdef-1234-567890abcdef"
Step 2: Construct the Participant Update Payload
The Genesys Cloud API uses a PATCH request to update participant attributes. The body of this request must contain a attributes object. These attributes are key-value pairs that are stored with the participant record and can be used for routing, analytics, or downstream integrations.
Important: The attributes object is free-form JSON. You must define the keys you intend to use. Common use cases include:
customer_id: Link to your CRM.queue_priority: Custom priority logic.sentiment_score: Real-time sentiment analysis result.
def build_participant_update_body(new_attributes: dict) -> dict:
"""
Builds the JSON body for the PATCH request.
"""
return {
"attributes": new_attributes
}
Example payload for updating a customer ID and a custom tag:
{
"attributes": {
"crm_customer_id": "CRM-998877",
"vip_status": true,
"last_interaction_type": "support_ticket"
}
}
Step 3: Execute the Update via SDK
The patch_conversations_conversation_participant method in the Python SDK handles the HTTP PATCH request. You must pass the conversation_id, participant_id, and the body.
def update_participant_attributes(
conversations_api: ConversationsApi,
conversation_id: str,
participant_id: str,
new_attributes: dict
) -> dict:
"""
Updates the attributes of a specific participant in a conversation.
"""
try:
# Construct the request body
body = build_participant_update_body(new_attributes)
# Execute the PATCH request
# The SDK returns the updated participant object
response = conversations_api.patch_conversations_conversation_participant(
conversation_id=conversation_id,
participant_id=participant_id,
body=body
)
logging.info(f"Successfully updated attributes for participant {participant_id}")
return response.to_dict()
except Exception as e:
# Handle specific HTTP errors
if hasattr(e, 'status') and e.status == 404:
logging.error(f"Conversation or Participant not found: {conversation_id}, {participant_id}")
elif hasattr(e, 'status') and e.status == 403:
logging.error(f"Forbidden: Check OAuth scopes. Missing 'conversation:participant:write'?")
elif hasattr(e, 'status') and e.status == 429:
logging.error(f"Rate limited. Implement retry logic.")
else:
logging.error(f"Unexpected error updating participant: {e}")
raise
Step 4: Handling Rate Limits (429 Errors)
Genesys Cloud APIs enforce rate limits. If you are updating attributes frequently (e.g., every time sentiment changes), you may hit the limit. You must implement exponential backoff.
import time
import random
def update_with_retry(
conversations_api: ConversationsApi,
conversation_id: str,
participant_id: str,
new_attributes: dict,
max_retries: int = 3
) -> dict:
"""
Updates participant attributes with exponential backoff for 429 errors.
"""
for attempt in range(max_retries):
try:
return update_participant_attributes(
conversations_api,
conversation_id,
participant_id,
new_attributes
)
except Exception as e:
# Check if it is a 429 Too Many Requests error
if hasattr(e, 'status') and e.status == 429:
wait_time = (2 ** attempt) + random.uniform(0, 1)
logging.warning(f"Rate limited (429). Retrying in {wait_time:.2f} seconds...")
time.sleep(wait_time)
else:
# Non-retryable error
raise
raise Exception("Max retries exceeded for updating participant attributes.")
Complete Working Example
This script demonstrates the full flow: authentication, constructing the payload, updating the attributes, and error handling.
import os
import logging
import time
import random
from genesyscloud import Configuration, ApiClient, ConversationsApi
from dotenv import load_dotenv
# Configure logging
logging.basicConfig(level=logging.INFO)
def setup_client():
load_dotenv()
configuration = Configuration()
configuration.host = f"https://api.{os.getenv('GENESYS_REGION')}.mypurecloud.com"
api_client = ApiClient(configuration)
try:
api_client.login(
client_id=os.getenv('GENESYS_CLIENT_ID'),
client_secret=os.getenv('GENESYS_CLIENT_SECRET')
)
except Exception as e:
logging.error(f"Authentication failed: {e}")
raise SystemExit("Authentication failed.")
return ConversationsApi(api_client)
def update_participant_attributes(
conversations_api: ConversationsApi,
conversation_id: str,
participant_id: str,
new_attributes: dict,
max_retries: int = 3
) -> dict:
"""
Updates the attributes of a specific participant in a conversation.
Includes retry logic for 429 errors.
"""
for attempt in range(max_retries):
try:
# Construct the body
body = {
"attributes": new_attributes
}
logging.info(f"Attempt {attempt + 1}: Updating attributes for participant {participant_id}")
# Execute the PATCH request
response = conversations_api.patch_conversations_conversation_participant(
conversation_id=conversation_id,
participant_id=participant_id,
body=body
)
logging.info("Update successful.")
return response.to_dict()
except Exception as e:
# Check for 429 Rate Limit
if hasattr(e, 'status') and e.status == 429:
wait_time = (2 ** attempt) + random.uniform(0, 1)
logging.warning(f"Rate limited (429). Waiting {wait_time:.2f}s before retry.")
time.sleep(wait_time)
continue
elif hasattr(e, 'status') and e.status == 404:
logging.error(f"Not Found: Conversation {conversation_id} or Participant {participant_id} does not exist.")
return None
elif hasattr(e, 'status') and e.status == 403:
logging.error(f"Forbidden: Ensure OAuth client has 'conversation:participant:write' scope.")
return None
else:
logging.error(f"Unexpected error: {e}")
raise
logging.error("Max retries exceeded.")
return None
def main():
# 1. Setup Client
conversations_api = setup_client()
# 2. Define Target
# Replace these with real IDs from your environment
TARGET_CONVERSATION_ID = os.getenv('TEST_CONVERSATION_ID', "replace-with-real-id")
TARGET_PARTICIPANT_ID = os.getenv('TEST_PARTICIPANT_ID', "replace-with-real-id")
# 3. Define New Attributes
# These are arbitrary key-value pairs stored in Genesys Cloud
NEW_ATTRIBUTES = {
"external_system_id": "EXT-12345",
"risk_score": 0.85,
"preferred_channel": "chat"
}
# 4. Execute Update
result = update_participant_attributes(
conversations_api=conversations_api,
conversation_id=TARGET_CONVERSATION_ID,
participant_id=TARGET_PARTICIPANT_ID,
new_attributes=NEW_ATTRIBUTES
)
if result:
print("Final Participant State:")
print(result.get('attributes', {}))
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden
- What causes it: The OAuth token used in the request does not have the
conversation:participant:writescope. - How to fix it: Go to the Genesys Cloud Admin Console. Navigate to Organization > OAuth 2.0 > Applications. Select your application. In the Scopes tab, ensure
conversation:participant:writeis checked. Save and regenerate your token.
Error: 404 Not Found
- What causes it: The
conversation_idorparticipant_idis invalid, expired, or belongs to a different Genesys Cloud organization. - How to fix it: Verify the IDs are correct. Ensure the conversation is still active or recently completed (historical data is accessible, but mid-conversation updates only apply to active/queued states).
Error: 429 Too Many Requests
- What causes it: You have exceeded the API rate limit for your organization or tenant.
- How to fix it: Implement exponential backoff (as shown in the complete example). Do not retry immediately. Spread out your requests. If you need higher throughput, contact Genesys Cloud Support to discuss rate limit increases.
Error: 400 Bad Request
- What causes it: The JSON body is malformed or the
attributesobject contains invalid data types. - How to fix it: Ensure the body is a valid JSON object with an
attributeskey. The values insideattributesmust be strings, numbers, booleans, or null. Nested objects are supported but must be valid JSON.