Programmatic Wrap-Up Codes in Genesys Cloud CX
What You Will Build
- A script that programmatically assigns a specific wrap-up code to a conversation immediately after it concludes.
- This tutorial uses the Genesys Cloud CX Conversation API and the Wrap-Up Code API.
- The implementation covers Python and JavaScript.
Prerequisites
- OAuth Client Type: Service Account (Client Credentials Grant) or User Account (Authorization Code Grant).
- Required Scopes:
conversation:conversation:write(to update the conversation wrap-up).wrapupcode:view(to retrieve available wrap-up codes).
- SDK Version:
- Python:
genesys-cloud-sdk-pythonv140.0.0+ - JavaScript:
@genesyscloud/api-architecture-apisv140.0.0+
- Python:
- Runtime Requirements:
- Python 3.9+
- Node.js 18+
Authentication Setup
Genesys Cloud CX APIs require a valid OAuth 2.0 bearer token. For background automation tasks like setting wrap-up codes, a Service Account is the standard approach. You must configure the Service Account with the necessary permissions in the Genesys Cloud Admin Console before running this code.
Python Authentication
Use the genesys-cloud-sdk-python library to handle token acquisition. The SDK manages the token refresh lifecycle automatically if configured correctly.
import os
from purecloudplatformclientv2 import Configuration, ApiClient, ConversationApi, WrapupcodeApi
def get_auth_config():
"""
Sets up the Genesys Cloud configuration with Service Account credentials.
"""
config = Configuration()
config.host = "https://api.mypurecloud.com"
config.oauth_client_id = os.getenv("GENESYS_CLIENT_ID")
config.oauth_client_secret = os.getenv("GENESYS_CLIENT_SECRET")
return config
def initialize_apis(config):
"""
Initializes the API clients with the configuration.
"""
api_client = ApiClient(configuration=config)
conversation_api = ConversationApi(api_client)
wrapupcode_api = WrapupcodeApi(api_client)
return conversation_api, wrapupcode_api
JavaScript Authentication
In JavaScript, use the @genesyscloud/api-architecture-apis package. This package provides a unified client that handles authentication and API calls.
const {
Configuration,
ConversationApi,
WrapupcodeApi
} = require("@genesyscloud/api-architecture-apis");
require("dotenv").config();
async function initClient() {
const config = new Configuration({
basePath: "https://api.mypurecloud.com",
clientId: process.env.GENESYS_CLIENT_ID,
clientSecret: process.env.GENESYS_CLIENT_SECRET
});
const conversationApi = new ConversationApi(config);
const wrapupcodeApi = new WrapupcodeApi(config);
return { conversationApi, wrapupcodeApi };
}
Implementation
Setting a wrap-up code programmatically requires two distinct actions. First, you must identify the correct wrap-up code object. Second, you must update the specific conversation instance with that code.
Step 1: Retrieve the Wrap-Up Code ID
Genesys Cloud CX does not accept wrap-up code names or descriptions in the conversation update payload. It strictly requires the unique string identifier (id) of the wrap-up code. You must query the Wrap-Up Code API to find the ID corresponding to the desired code.
Wrap-up codes are scoped to a specific routing queue. You cannot use a wrap-up code from Queue A in a conversation routed to Queue B. Therefore, your query must filter by the queueId.
Python: Finding the Wrap-Up Code
def find_wrapup_code_id(wrapupcode_api, queue_id, code_name):
"""
Searches for a wrap-up code by name within a specific queue.
"""
try:
# Fetch all wrap-up codes for the queue
# limit is required, default is 25. Increase if necessary.
response = wrapupcode_api.post_routing_wrapupcodes_search(
body={
"query": {
"filters": [
{
"name": "queueId",
"values": [queue_id]
},
{
"name": "name",
"values": [code_name]
}
],
"limit": 100
}
}
)
if response.entities and len(response.entities) > 0:
return response.entities[0].id
else:
raise ValueError(f"Wrap-up code '{code_name}' not found in queue '{queue_id}'")
except Exception as e:
print(f"Error retrieving wrap-up code: {e}")
raise
JavaScript: Finding the Wrap-Up Code
async function findWrapupCodeId(wrapupcodeApi, queueId, codeName) {
try {
const body = {
query: {
filters: [
{ name: "queueId", values: [queueId] },
{ name: "name", values: [codeName] }
],
limit: 100
}
};
const response = await wrapupcodeApi.postRoutingWrapupcodesSearch(body);
if (response.entities && response.entities.length > 0) {
return response.entities[0].id;
} else {
throw new Error(`Wrap-up code '${codeName}' not found in queue '${queueId}'`);
}
} catch (error) {
console.error("Error retrieving wrap-up code:", error);
throw error;
}
}
Step 2: Update the Conversation with the Wrap-Up Code
Once you have the wrapupCodeId, you must update the conversation. The Genesys Cloud CX Conversation API uses the HTTP PATCH method to update specific fields of a resource. You target the endpoint /api/v2/conversations/{conversationId}.
The payload for the wrap-up update is a JSON object containing the wrapup field. This field is an object that maps the wrap-up code ID to an optional duration and text comment. If you do not provide a duration, the system uses the default duration defined in the wrap-up code configuration.
Python: Applying the Wrap-Up Code
def set_wrapup_code(conversation_api, conversation_id, wrapup_code_id, agent_id):
"""
Sets the wrap-up code for a specific conversation.
Args:
conversation_api: Initialized ConversationApi client
conversation_id: The UUID of the conversation
wrapup_code_id: The UUID of the wrap-up code
agent_id: The UUID of the agent who is wrapping up
"""
try:
# Construct the wrap-up object
# The key is the wrap-up code ID.
# The value is an object with optional 'duration' (in seconds) and 'text'.
wrapup_body = {
"wrapup": {
wrapup_code_id: {
"duration": 60, # Example: 60 seconds of wrap-up time
"text": "Automated wrap-up applied via API"
}
},
"wrapupBy": {
"id": agent_id
}
}
# Execute the PATCH request
response = conversation_api.patch_conversations_conversation(
conversation_id=conversation_id,
body=wrapup_body
)
print(f"Successfully set wrap-up code for conversation {conversation_id}")
return response
except Exception as e:
print(f"Error setting wrap-up code: {e}")
raise
JavaScript: Applying the Wrap-Up Code
async function setWrapupCode(conversationApi, conversationId, wrapupCodeId, agentId) {
try {
const body = {
wrapup: {
[wrapupCodeId]: {
duration: 60, // Example: 60 seconds
text: "Automated wrap-up applied via API"
}
},
wrapupBy: {
id: agentId
}
};
const response = await conversationApi.patchConversationsConversation(
conversationId,
body
);
console.log(`Successfully set wrap-up code for conversation ${conversationId}`);
return response;
} catch (error) {
console.error("Error setting wrap-up code:", error);
throw error;
}
}
Step 3: Handling Conversation State and Timing
A critical constraint in Genesys Cloud CX is that you can only set a wrap-up code on a conversation that is in a state allowing it. Typically, this means the conversation must be closed or queued for wrap-up. If you attempt to set a wrap-up code on an active conversation, the API will return a 400 Bad Request.
To ensure reliability, your integration should listen for the conversation:wrapup:started event via Websockets or Polling, or trigger the API call immediately after your application logic closes the conversation.
If you are using the Genesys Cloud Websockets API, you can subscribe to conversation events. When you receive an event with type: "wrapup", you can verify if a wrap-up code is already set. If not, you can apply your default code.
# Conceptual Websocket Listener Integration
def on_conversation_event(event):
if event.get("type") == "wrapup":
conversation_id = event.get("id")
current_wrapup = event.get("wrapup")
# Check if a wrap-up code is already assigned
if not current_wrapup:
print(f"Conversation {conversation_id} entered wrap-up without a code. Applying default.")
# Trigger the set_wrapup_code function here
# set_wrapup_code(api, conversation_id, default_code_id, agent_id)
else:
print(f"Conversation {conversation_id} already has a wrap-up code.")
Complete Working Example
The following Python script combines authentication, code retrieval, and application into a single executable module. It includes error handling for missing codes and API failures.
import os
import sys
from purecloudplatformclientv2 import Configuration, ApiClient, ConversationApi, WrapupcodeApi, ApiException
def main():
# 1. Configuration
config = Configuration()
config.host = "https://api.mypurecloud.com"
config.oauth_client_id = os.getenv("GENESYS_CLIENT_ID")
config.oauth_client_secret = os.getenv("GENESYS_CLIENT_SECRET")
if not config.oauth_client_id or not config.oauth_client_secret:
print("Error: GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET environment variables are required.")
sys.exit(1)
api_client = ApiClient(configuration=config)
conversation_api = ConversationApi(api_client)
wrapupcode_api = WrapupcodeApi(api_client)
# 2. Input Parameters
conversation_id = os.getenv("TARGET_CONVERSATION_ID")
queue_id = os.getenv("TARGET_QUEUE_ID")
wrapup_code_name = os.getenv("TARGET_WRAPUP_CODE_NAME", "Default Wrapup")
agent_id = os.getenv("AGENT_ID")
if not conversation_id or not queue_id or not agent_id:
print("Error: Missing required environment variables (TARGET_CONVERSATION_ID, TARGET_QUEUE_ID, AGENT_ID).")
sys.exit(1)
try:
# 3. Retrieve Wrap-Up Code ID
print(f"Searching for wrap-up code '{wrapup_code_name}' in queue '{queue_id}'...")
search_body = {
"query": {
"filters": [
{"name": "queueId", "values": [queue_id]},
{"name": "name", "values": [wrapup_code_name]}
],
"limit": 100
}
}
search_response = wrapupcode_api.post_routing_wrapupcodes_search(body=search_body)
if not search_response.entities or len(search_response.entities) == 0:
print(f"Error: Wrap-up code '{wrapup_code_name}' not found in queue '{queue_id}'.")
sys.exit(1)
wrapup_code_id = search_response.entities[0].id
print(f"Found wrap-up code ID: {wrapup_code_id}")
# 4. Apply Wrap-Up Code
print(f"Applying wrap-up code to conversation {conversation_id}...")
update_body = {
"wrapup": {
wrapup_code_id: {
"duration": 30, # 30 seconds
"text": "Applied by automated script"
}
},
"wrapupBy": {
"id": agent_id
}
}
conversation_api.patch_conversations_conversation(
conversation_id=conversation_id,
body=update_body
)
print("Success: Wrap-up code applied.")
except ApiException as e:
print(f"API Error: {e.status} - {e.reason}")
print(f"Response Body: {e.body}")
sys.exit(1)
except Exception as e:
print(f"Unexpected Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 400 Bad Request - Invalid Wrap-Up Code
Cause: The wrap-up code ID provided in the payload does not exist, or it does not belong to the queue associated with the conversation.
Fix: Verify that the wrapupCodeId was retrieved using the queueId of the conversation. Genesys Cloud CX enforces strict scoping. A wrap-up code from Queue A cannot be applied to a conversation in Queue B.
Debugging Code:
# Before applying, verify the queue ID matches
conv_details = conversation_api.get_conversations_conversation(conversation_id)
if conv_details.routing.queue.id != target_queue_id:
raise ValueError("Conversation queue does not match target queue.")
Error: 403 Forbidden - Insufficient Permissions
Cause: The OAuth token used does not have the conversation:conversation:write scope.
Fix: Regenerate the OAuth token with the correct scopes. Ensure the Service Account has the “Edit Conversation” permission in the Admin Console.
Error: 409 Conflict - Conversation Already Wrapped Up
Cause: The conversation already has a wrap-up code assigned, and you are attempting to overwrite it without clearing it first. While Genesys Cloud CX allows updating wrap-up codes, some client-side states or concurrent modifications can cause conflicts.
Fix: Check the current state of the conversation before applying the code. If a wrap-up code is already present, decide whether to update it or skip the action.
# Check existing wrap-up
conv = conversation_api.get_conversations_conversation(conversation_id)
if conv.wrapup:
print("Conversation already has a wrap-up code.")
# Logic to handle existing code
Error: 429 Too Many Requests
Cause: Your application has exceeded the API rate limits.
Fix: Implement exponential backoff retry logic. The Genesys Cloud CX SDKs do not automatically retry 429 errors for all operations, so you must handle this in your application code.
Retry Logic Example:
import time
def retry_with_backoff(func, *args, max_retries=3, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except ApiException as e:
if e.status == 429:
wait_time = 2 ** attempt
print(f"Rate limited. Retrying in {wait_time} seconds...")
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")
# Usage
# retry_with_backoff(conversation_api.patch_conversations_conversation, conversation_id, body)