Programmatically Setting Wrap-Up Codes in Genesys Cloud CX
What You Will Build
- You will build a Python script that automatically assigns a specific wrap-up code to an interaction immediately after it concludes.
- This tutorial uses the Genesys Cloud CX API v2, specifically the Interactions and Analytics endpoints.
- The implementation is written in Python 3.9+ using the
genesyscloudSDK andrequestsfor direct API calls where SDK coverage is limited.
Prerequisites
- OAuth Client Type: Machine-to-Machine (M2M) credentials.
- Required Scopes:
interaction:wrapupcode:write(To assign the wrap-up code)interaction:interaction:read(To query interaction status)analytics:conversation:details:read(Optional, for verification)
- SDK Version:
genesyscloudPython SDK v2.0.0 or later. - Language/Runtime: Python 3.9+
- External Dependencies:
genesyscloud(Official SDK)requests(For HTTP interactions if SDK methods are insufficient)time(Standard library, for polling logic)
Authentication Setup
Genesys Cloud uses OAuth 2.0 for API authentication. For server-side automation like setting wrap-up codes, you must use the Client Credentials Grant flow. This requires an OAuth client ID and secret generated in the Genesys Cloud Admin Console.
The following code initializes the Genesys Cloud platform client. This client handles token acquisition and refreshing automatically.
import os
from genesyscloud.platform.client import PlatformClient
from genesyscloud.platform.client.configuration import Configuration
def get_platform_client() -> PlatformClient:
"""
Initializes and returns a configured Genesys Cloud PlatformClient.
Returns:
PlatformClient: The configured client instance.
"""
# Retrieve credentials from environment variables
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
region_host = os.getenv("GENESYS_REGION_HOST", "us-east-1.mygen.com")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET must be set.")
# Create configuration object
config = Configuration()
config.region_host = region_host
# Set up OAuth credentials
config.oauth_client_id = client_id
config.oauth_client_secret = client_secret
# Initialize the platform client
client = PlatformClient(config)
# Verify connection by fetching current user info (optional but recommended for debugging)
try:
client.user_api.get_user_me()
except Exception as e:
print(f"Authentication failed: {e}")
raise e
return client
# Initialize the client globally or pass it to functions
pc = get_platform_client()
Error Handling Note: If you receive a 401 Unauthorized error here, verify that your Client ID and Secret are correct and that the OAuth client has the necessary scopes enabled in the Admin Console under Admin > Security > OAuth Clients.
Implementation
Step 1: Identify the Interaction
To set a wrap-up code, you need the interactionId. This ID is unique to the conversation and is available in several ways:
- Passed from a webhook triggered by the
conversation:wrapupevent. - Retrieved via the Analytics API by filtering on
participantIdorexternalContactId. - Stored in your CRM system during the initial interaction creation.
For this tutorial, we assume you already have the interactionId. If you do not, you must query the Analytics API first. Below is a helper function to fetch interactions for a specific user within the last hour, which is a common pattern for finding recent interactions.
from genesyscloud.analytics.models import ConversationDetailsQuery
from datetime import datetime, timedelta
def get_recent_interactions(user_id: str, platform_client: PlatformClient) -> list:
"""
Retrieves recent interactions for a specific user.
Args:
user_id (str): The ID of the user agent.
platform_client (PlatformClient): The initialized platform client.
Returns:
list: A list of interaction IDs.
"""
# Define the query parameters
# We look for interactions ending in the last 60 minutes
end_time = datetime.utcnow()
start_time = end_time - timedelta(hours=1)
query = ConversationDetailsQuery(
view="realtime",
interval=f"{start_time.isoformat()}Z/{end_time.isoformat()}Z",
entity=[
{
"type": "conversation",
"path": "participants",
"selector": "participantId",
"values": [user_id]
}
],
size=10
)
try:
# Call the analytics API
response = platform_client.analytics_api.post_analytics_conversations_details_query(body=query)
if response.entities and len(response.entities) > 0:
interaction_ids = [ent.conversation_id for ent in response.entities]
return interaction_ids
else:
print("No recent interactions found.")
return []
except Exception as e:
print(f"Error fetching interactions: {e}")
return []
OAuth Scope: analytics:conversation:details:read
Key Parameter Explanation:
view: Set torealtimefor recent data. Usesummaryfor historical data.interval: ISO 8601 formatted start and end times.entity: Filters the data. Here, we filter byparticipantIdto find interactions involving a specific agent.
Step 2: Retrieve Available Wrap-Up Codes
You cannot assign a wrap-up code ID that does not exist or is not available to the user. You must first fetch the list of wrap-up codes. Wrap-up codes are associated with a specific “Wrap-up Code Group”. You need to know which group to query.
If you do not know the group ID, you can list all groups. For this example, we will assume you have the wrapup_code_group_id.
def get_wrapup_codes(group_id: str, platform_client: PlatformClient) -> dict:
"""
Fetches all wrap-up codes in a specific group.
Args:
group_id (str): The ID of the wrap-up code group.
platform_client (PlatformClient): The initialized platform client.
Returns:
dict: A dictionary mapping code name to code ID.
"""
try:
# Get the list of wrap-up codes
response = platform_client.wfm_scheduling_api.get_wfm_scheduling_wrapupcodegroups_wrapupcodes(
wrapupcodegroupid=group_id
)
codes_map = {}
if response.entities:
for code in response.entities:
codes_map[code.name] = code.id
print(f"Found wrap-up code: {code.name} (ID: {code.id})")
return codes_map
except Exception as e:
print(f"Error fetching wrap-up codes: {e}")
return {}
OAuth Scope: interaction:wrapupcode:read
Edge Case: Wrap-up codes can be hidden or disabled. The API returns all codes in the group, but the UI might filter them. Ensure the code you intend to use is not disabled in the Admin Console.
Step 3: Assign the Wrap-Up Code Programmatically
This is the core action. You use the PUT /api/v2/interactions/{interactionId}/wrapup endpoint. The body requires the wrapupCodeId and optionally a duration (in milliseconds) if you want to log time spent in the wrap-up state.
Important: You can only set a wrap-up code on an interaction that is in the wrapup state or has recently ended. If the interaction is still connected, the API will reject the request.
from genesyscloud.interactions.models import InteractionWrapup
def set_wrapup_code(interaction_id: str, wrapup_code_id: str, platform_client: PlatformClient) -> bool:
"""
Sets a wrap-up code on a specific interaction.
Args:
interaction_id (str): The unique ID of the interaction.
wrapup_code_id (str): The ID of the wrap-up code to assign.
platform_client (PlatformClient): The initialized platform client.
Returns:
bool: True if successful, False otherwise.
"""
# Construct the wrap-up body
wrapup_body = InteractionWrapup(
wrapup_code_id=wrapup_code_id,
# Optional: Duration in milliseconds.
# If omitted, the system calculates it from the start of wrap-up to now.
duration=0
)
try:
# Execute the API call
response = platform_client.interactions_api.put_interactions_interaction_wrapup(
interaction_id=interaction_id,
body=wrapup_body
)
print(f"Wrap-up code set successfully for interaction {interaction_id}")
print(f"Response ID: {response.id}")
return True
except platform_client.exceptions.ApiException as e:
# Handle specific HTTP status codes
if e.status == 409:
print(f"Conflict: Interaction {interaction_id} is not in a valid state for wrap-up.")
elif e.status == 404:
print(f"Not Found: Interaction {interaction_id} does not exist.")
elif e.status == 400:
print(f"Bad Request: Invalid wrap-up code ID or format.")
else:
print(f"API Error {e.status}: {e.reason}")
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
OAuth Scope: interaction:wrapupcode:write
Realistic Response Body:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"selfUri": "/api/v2/interactions/a1b2c3d4-e5f6-7890-abcd-ef1234567890/wrapup"
}
Complete Working Example
This script ties everything together. It assumes you have an interactionId and a wrapupCodeGroupId. It fetches the codes, finds a specific one by name, and assigns it.
import os
import sys
from genesyscloud.platform.client import PlatformClient
from genesyscloud.platform.client.configuration import Configuration
from genesyscloud.interactions.models import InteractionWrapup
def main():
# 1. Configuration
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
region_host = os.getenv("GENESYS_REGION_HOST", "us-east-1.mygen.com")
# Hardcoded for example - in production, retrieve these dynamically
INTERACTION_ID = "your-interaction-id-here"
WRAPUP_GROUP_ID = "your-wrapup-group-id-here"
TARGET_CODE_NAME = "Sale Completed"
if not client_id or not client_secret:
print("Error: Missing environment variables GENESYS_CLIENT_ID or GENESYS_CLIENT_SECRET")
sys.exit(1)
# 2. Initialize Client
config = Configuration()
config.region_host = region_host
config.oauth_client_id = client_id
config.oauth_client_secret = client_secret
pc = PlatformClient(config)
try:
# 3. Fetch Wrap-Up Codes
print(f"Fetching wrap-up codes from group {WRAPUP_GROUP_ID}...")
codes_response = pc.wfm_scheduling_api.get_wfm_scheduling_wrapupcodegroups_wrapupcodes(
wrapupcodegroupid=WRAPUP_GROUP_ID
)
target_code_id = None
if codes_response.entities:
for code in codes_response.entities:
if code.name == TARGET_CODE_NAME:
target_code_id = code.id
break
if not target_code_id:
print(f"Error: Wrap-up code '{TARGET_CODE_NAME}' not found in group {WRAPUP_GROUP_ID}")
sys.exit(1)
print(f"Found wrap-up code ID: {target_code_id}")
# 4. Set Wrap-Up Code
print(f"Setting wrap-up code on interaction {INTERACTION_ID}...")
wrapup_body = InteractionWrapup(
wrapup_code_id=target_code_id
)
response = pc.interactions_api.put_interactions_interaction_wrapup(
interaction_id=INTERACTION_ID,
body=wrapup_body
)
print(f"Success! Wrap-up code assigned. Response ID: {response.id}")
except Exception as e:
print(f"An error occurred: {e}")
if hasattr(e, 'status'):
print(f"HTTP Status: {e.status}")
print(f"Response Body: {e.body}")
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 409 Conflict
What causes it:
The interaction is not in a state that allows a wrap-up code to be set. This usually happens if:
- The interaction is still
connected(active). - The interaction has already been wrapped up and closed.
- The interaction is in the
holdstate.
How to fix it:
Check the interaction status before attempting to set the wrap-up code.
def check_interaction_status(interaction_id: str, pc: PlatformClient):
try:
interaction = pc.interactions_api.get_interactions_interaction(interaction_id)
print(f"Current State: {interaction.state}")
print(f"Current Wrap-up Code ID: {interaction.wrapup_code_id}")
return interaction.state
except Exception as e:
print(f"Error checking status: {e}")
Only proceed if interaction.state is wrapup or if the interaction has just moved to wrapup from connected.
Error: 403 Forbidden
What causes it:
The OAuth client does not have the required scope interaction:wrapupcode:write.
How to fix it:
- Go to Genesys Cloud Admin Console.
- Navigate to Admin > Security > OAuth Clients.
- Select your client.
- Go to the Scopes tab.
- Add
interaction:wrapupcode:write. - Save the changes. Note: Existing tokens may need to be refreshed.
Error: 429 Too Many Requests
What causes it:
You are hitting the API rate limit. Genesys Cloud enforces strict rate limits (e.g., 100 requests per second for some endpoints).
How to fix it:
Implement exponential backoff retry logic.
import time
def retry_with_backoff(func, *args, max_retries=3, initial_delay=1, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if hasattr(e, 'status') and e.status == 429:
delay = initial_delay * (2 ** attempt)
print(f"Rate limited. Retrying in {delay} seconds...")
time.sleep(delay)
else:
raise e
raise Exception("Max retries exceeded")
Error: 400 Bad Request
What causes it:
The wrapupCodeId provided is invalid, or the InteractionWrapup body is malformed.
How to fix it:
Verify that the wrapupCodeId exists in the specified group. Print the wrapup_code_id before sending the request to ensure it is not None or an empty string.