How to use the Set Participant Data action to pass custom variables through an IVR flow
What You Will Build
- You will build a Python script that updates participant attributes in an active Genesys Cloud conversation, enabling dynamic IVR routing based on real-time data.
- You will use the Genesys Cloud Platform API v2 (
/api/v2/conversations/{conversationId}/participants/{participantId}) and thePureCloudPlatformClientV2Python SDK. - You will implement this using Python 3.9+ with the
purecloud-platform-client-v2library.
Prerequisites
- OAuth Client Type: A Genesys Cloud OAuth Client with the
confidentialgrant type. - Required Scopes:
conversation:participant:write(To update participant data)conversation:read(To inspect conversation details if needed)user:read(Optional, for debugging user contexts)
- SDK Version:
purecloud-platform-client-v2version 130.0.0 or higher. - Runtime: Python 3.9 or higher.
- Dependencies:
purecloud-platform-client-v2requests(for underlying HTTP handling, managed by SDK)
Install the SDK via pip:
pip install purecloud-platform-client-v2
Authentication Setup
Genesys Cloud uses OAuth 2.0 for authentication. For server-to-server integrations (like an IVR backend service), you must use the Client Credentials flow. The SDK handles token caching and refresh automatically if configured correctly, but you must provide the initial credentials.
Create a configuration object that points to your Genesys Cloud environment (e.g., mypurecloud.com for US, mypurecloud.ie for EU).
import purecloud_platform_client_v2 as platform_client
from purecloud_platform_client_v2.configuration import Configuration
from purecloud_platform_client_v2.api_client import ApiClient
from purecloud_platform_client_v2.rest import ApiException
def get_genesys_api_client(client_id: str, client_secret: str, env: str = "mypurecloud.com") -> ApiClient:
"""
Initializes and returns a configured Genesys Cloud API Client.
Args:
client_id: OAuth Client ID
client_secret: OAuth Client Secret
env: Genesys Cloud environment domain (default: mypurecloud.com)
Returns:
ApiClient: Configured API client instance
"""
# Define the OAuth configuration
oauth_config = platform_client.OAuthClientCredentials(
client_id=client_id,
client_secret=client_secret,
base_url=f"https://{env}"
)
# Create the main configuration object
config = Configuration(
host=f"https://{env}",
oauth_client=oauth_config
)
# Initialize the API client with the configuration
api_client = ApiClient(configuration=config)
return api_client
# Example usage
# api_client = get_genesys_api_client("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
This setup ensures that every subsequent API call includes a valid Authorization: Bearer <token> header. The SDK manages the token lifecycle, so you do not need to manually handle refresh tokens in this context.
Implementation
Step 1: Identify the Conversation and Participant
Before you can set participant data, you must know the conversationId and the participantId. In an IVR context, these are typically passed from the IVR flow to your backend service via HTTP POST or retrieved via the Conversations API.
If you are triggering this from an IVR flow using the “Invoke HTTP Request” action, the IVR provides the context in the request headers or body. However, if you are building a proactive update service, you must query for the active conversation.
Here is how you retrieve an active conversation for a specific user or endpoint. For this tutorial, we assume you have the conversationId and participantId available, as is common when an IVR flow calls a webhook to update data mid-flow.
from purecloud_platform_client_v2.api.conversations_api import ConversationsApi
from purecloud_platform_client_v2.model.update_participant import UpdateParticipant
from purecloud_platform_client_v2.model.conversation_participant_attributes import ConversationParticipantAttributes
def get_active_conversation(api_client: ApiClient, conversation_id: str, participant_id: str) -> dict:
"""
Validates that the conversation and participant exist.
Args:
api_client: The configured API client
conversation_id: ID of the Genesys Cloud conversation
participant_id: ID of the participant within that conversation
Returns:
dict: Participant details
Raises:
ApiException: If the conversation or participant is not found
"""
conversations_api = ConversationsApi(api_client)
try:
# Retrieve the specific participant to validate existence
participant = conversations_api.get_conversation_participant(
conversation_id=conversation_id,
participant_id=participant_id
)
return participant.to_dict()
except ApiException as e:
if e.status == 404:
print(f"Error: Conversation {conversation_id} or Participant {participant_id} not found.")
else:
print(f"Unexpected error: {e.status} - {e.reason}")
raise
Step 2: Construct the Update Participant Payload
The core of this tutorial is the PATCH /api/v2/conversations/{conversationId}/participants/{participantId} endpoint. This endpoint accepts a UpdateParticipant object.
The critical field is attributes. This is a dictionary (JSON object) where you store custom key-value pairs. These attributes persist for the duration of the conversation and are accessible to other IVR actions, such as “Set Participant Data” (if configured to read from this source) or routing strategies.
Important: The attributes field is not created by default. You must explicitly define it. If you send an empty object {}, it clears all existing attributes. If you omit the field entirely, existing attributes remain unchanged. To update specific keys while preserving others, you must first retrieve the current attributes, merge your new data, and send the full object back.
from purecloud_platform_client_v2.model.update_participant import UpdateParticipant
from purecloud_platform_client_v2.model.conversation_participant_attributes import ConversationParticipantAttributes
def build_update_payload(current_attributes: dict, new_data: dict) -> UpdateParticipant:
"""
Constructs the UpdateParticipant object with merged attributes.
Args:
current_attributes: Existing participant attributes (dict)
new_data: New key-value pairs to add/update
Returns:
UpdateParticipant: Object ready for API submission
"""
# Merge existing attributes with new data
# This ensures we do not overwrite unrelated custom variables
merged_attributes = current_attributes.copy()
merged_attributes.update(new_data)
# Create the ConversationParticipantAttributes object
participant_attrs = ConversationParticipantAttributes(attributes=merged_attributes)
# Create the UpdateParticipant object
# Note: We only set 'attributes'. Other fields like 'state' or 'routing'
# should not be touched unless you intend to change them.
update_participant = UpdateParticipant(
attributes=participant_attrs
)
return update_participant
Step 3: Execute the Update and Handle Errors
Now you combine the retrieval and update logic. You must handle the 409 Conflict error, which occurs if the participant state has changed since you last read it (optimistic locking). In high-throughput IVR scenarios, it is often safer to send a direct update without fetching first if you are only appending new data, but Genesys Cloud requires the full attributes object. Therefore, a fetch-then-update pattern is standard.
However, there is a nuance: if you only want to set a variable and do not care about existing ones, you can send a partial update. But the attributes field is treated as a full replacement. Thus, the merge step in Step 2 is critical.
def update_participant_data(
api_client: ApiClient,
conversation_id: str,
participant_id: str,
custom_vars: dict
) -> bool:
"""
Updates custom variables for a participant in an active conversation.
Args:
api_client: Configured API client
conversation_id: ID of the conversation
participant_id: ID of the participant
custom_vars: Dictionary of key-value pairs to set
Returns:
bool: True if successful, False otherwise
"""
conversations_api = ConversationsApi(api_client)
try:
# Step 1: Fetch current participant data to preserve existing attributes
participant = conversations_api.get_conversation_participant(
conversation_id=conversation_id,
participant_id=participant_id
)
# Extract existing attributes
existing_attrs = {}
if participant.attributes and participant.attributes.attributes:
existing_attrs = participant.attributes.attributes
# Step 2: Build the update payload
update_payload = build_update_payload(existing_attrs, custom_vars)
# Step 3: Execute the PATCH request
# Use _check_type=False to bypass strict type checking if needed,
# but standard usage is safe with the SDK models.
conversations_api.update_conversation_participant(
conversation_id=conversation_id,
participant_id=participant_id,
body=update_payload
)
print(f"Successfully updated attributes for participant {participant_id}")
return True
except ApiException as e:
if e.status == 409:
print("Conflict: The participant data has changed since last read. Retry logic recommended.")
# In production, implement exponential backoff here
elif e.status == 404:
print("Not Found: Conversation or Participant ID is invalid.")
elif e.status == 403:
print("Forbidden: Check OAuth scopes. Need 'conversation:participant:write'.")
else:
print(f"API Error: {e.status} - {e.reason}")
return False
except Exception as e:
print(f"Unexpected error: {str(e)}")
return False
Complete Working Example
This script demonstrates the full flow. It assumes you have an active conversation ID and participant ID. In a real IVR integration, these values come from the IVR runtime context.
import os
import purecloud_platform_client_v2 as platform_client
from purecloud_platform_client_v2.configuration import Configuration
from purecloud_platform_client_v2.api_client import ApiClient
from purecloud_platform_client_v2.api.conversations_api import ConversationsApi
from purecloud_platform_client_v2.model.update_participant import UpdateParticipant
from purecloud_platform_client_v2.model.conversation_participant_attributes import ConversationParticipantAttributes
from purecloud_platform_client_v2.rest import ApiException
def main():
# 1. Configuration
CLIENT_ID = os.getenv("GENESYS_CLIENT_ID")
CLIENT_SECRET = os.getenv("GENESYS_CLIENT_SECRET")
ENVIRONMENT = os.getenv("GENESYS_ENV", "mypurecloud.com")
if not CLIENT_ID or not CLIENT_SECRET:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET must be set.")
# Initialize API Client
oauth_config = platform_client.OAuthClientCredentials(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
base_url=f"https://{ENVIRONMENT}"
)
config = Configuration(host=f"https://{ENVIRONMENT}", oauth_client=oauth_config)
api_client = ApiClient(configuration=config)
# 2. Input Data
# Replace these with actual IDs from your IVR flow
CONVERSATION_ID = "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8"
PARTICIPANT_ID = "p9q8r7s6-t5u4-v3w2-x1y0-z9a8b7c6d5e4"
# Custom variables to set
# These keys must match the names used in your IVR flow for "Set Participant Data"
CUSTOM_DATA = {
"ivr_choice": "account_balance",
"priority_level": "high",
"customer_tier": "gold",
"session_id": "sess_123456789"
}
# 3. Execution
print(f"Starting update for Conversation {CONVERSATION_ID}, Participant {PARTICIPANT_ID}")
success = update_participant_data(api_client, CONVERSATION_ID, PARTICIPANT_ID, CUSTOM_DATA)
if success:
print("Operation completed successfully.")
# Optional: Verify the update
verify_update(api_client, CONVERSATION_ID, PARTICIPANT_ID)
else:
print("Operation failed.")
def update_participant_data(api_client, conversation_id, participant_id, custom_vars):
conversations_api = ConversationsApi(api_client)
try:
# Fetch current state
participant = conversations_api.get_conversation_participant(conversation_id, participant_id)
# Merge attributes
existing_attrs = {}
if participant.attributes and participant.attributes.attributes:
existing_attrs = participant.attributes.attributes.copy()
existing_attrs.update(custom_vars)
# Build payload
attrs_obj = ConversationParticipantAttributes(attributes=existing_attrs)
update_obj = UpdateParticipant(attributes=attrs_obj)
# Update
conversations_api.update_conversation_participant(conversation_id, participant_id, body=update_obj)
return True
except ApiException as e:
print(f"API Error {e.status}: {e.reason}")
return False
except Exception as e:
print(f"Error: {e}")
return False
def verify_update(api_client, conversation_id, participant_id):
"""Helper to print out the current attributes for debugging."""
conversations_api = ConversationsApi(api_client)
try:
participant = conversations_api.get_conversation_participant(conversation_id, participant_id)
if participant.attributes and participant.attributes.attributes:
print("Current Participant Attributes:")
for k, v in participant.attributes.attributes.items():
print(f" {k}: {v}")
except ApiException as e:
print(f"Verification failed: {e.reason}")
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden - “Insufficient permissions”
Cause: The OAuth client used in the script does not have the conversation:participant:write scope.
Fix:
- Log in to the Genesys Cloud Admin Portal.
- Navigate to Administration > Security > OAuth Clients.
- Select your client.
- Click Scopes.
- Ensure
conversation:participant:writeis checked. - Save and restart your application (or refresh the token).
Error: 409 Conflict - “The resource has been modified”
Cause: You attempted to update the participant data, but the participant object changed (e.g., another system updated attributes) between the time you fetched it and the time you sent the PATCH request. Genesys Cloud uses optimistic locking to prevent race conditions.
Fix:
Implement a retry mechanism. Fetch the latest state, merge your new data, and try again. Limit retries to 3 attempts to avoid infinite loops.
def update_with_retry(api_client, conversation_id, participant_id, custom_vars, max_retries=3):
for attempt in range(max_retries):
try:
return update_participant_data(api_client, conversation_id, participant_id, custom_vars)
except ApiException as e:
if e.status == 409 and attempt < max_retries - 1:
print(f"Conflict detected. Retrying attempt {attempt + 2}...")
import time
time.sleep(0.5 * (attempt + 1)) # Exponential backoff
continue
else:
raise
return False
Error: Attributes Not Visible in IVR
Cause: The key names in your API payload do not exactly match the variable names defined in the IVR flow.
Fix:
- Check the IVR flow designer.
- Locate the “Set Participant Data” action.
- Note the exact Variable Name (e.g.,
ivr_choice). - Ensure your Python dictionary key matches this exactly:
{"ivr_choice": "account_balance"}. - Case sensitivity matters.
IVR_Choiceis different fromivr_choice.
Error: 404 Not Found
Cause: The conversation_id or participant_id is invalid or the conversation has ended.
Fix:
- Verify the IDs are from an active conversation.
- If the conversation has ended, participant data is no longer updatable via this endpoint. You must use the Analytics API to retrieve historical data.
- Ensure you are using the correct environment (e.g.,
mypurecloud.comvsmypurecloud.ie).