How to Set Wrap-Up Codes Programmatically After an Interaction Ends

How to Set Wrap-Up Codes Programmatically After an Interaction Ends

What You Will Build

  • This tutorial demonstrates how to programmatically apply a wrap-up code to a completed conversation in Genesys Cloud.
  • It utilizes the Genesys Cloud Messaging API (/api/v2/conversations/messaging) and the Conversations API (/api/v2/conversations).
  • The primary implementation uses Python with the official genesyscloud SDK, with supplementary JavaScript examples for REST API integration.

Prerequisites

Authentication & Scopes

  • OAuth Client Type: Public or Confidential Client. A Confidential Client is recommended for server-to-server integrations.
  • Required Scopes:
    • conversation:read (to retrieve conversation details)
    • conversation:write (to update the wrap-up code)
    • user:read (optional, if resolving user IDs for agents)
  • Environment: A Genesys Cloud Organization with at least one active Messaging queue and configured Wrap-Up Codes.

SDK & Dependencies

  • Python: genesyscloud>=2.16.0
  • Node.js: axios (for REST examples)
  • Runtime: Python 3.9+ or Node.js 18+

Installation

# Python
pip install genesyscloud

# Node.js
npm install axios

Authentication Setup

Genesys Cloud uses OAuth 2.0 for authentication. For programmatic access, the Client Credentials flow is the standard approach. This flow exchanges a client ID and secret for an access token.

Python SDK Authentication

The genesyscloud SDK provides a PlatformClient class that handles token acquisition and refresh automatically. You must initialize the client with your environment base URL, client ID, and client secret.

from genesyscloud.platform.client import PlatformClient

def get_platform_client(client_id: str, client_secret: str, environment: str = "mypurecloud.com") -> PlatformClient:
    """
    Initializes and returns an authenticated Genesys Cloud PlatformClient.
    """
    # Construct the base URL
    base_url = f"https://{environment}"
    
    # Create the client
    client = PlatformClient(base_url=base_url)
    
    # Authenticate using Client Credentials
    client.login(client_id=client_id, client_secret=client_secret)
    
    return client

# Usage
# client = get_platform_client("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")

JavaScript (REST) Authentication

For environments where the full SDK is not desired, use Axios to manage the token lifecycle. Note that tokens expire after a set duration (typically 1 hour). A production implementation should implement token caching.

const axios = require('axios');

const GENESYS_BASE_URL = 'https://api.mypurecloud.com';
const CLIENT_ID = 'YOUR_CLIENT_ID';
const CLIENT_SECRET = 'YOUR_CLIENT_SECRET';

async function getAccessToken() {
    const response = await axios.post(`${GENESYS_BASE_URL}/oauth/token`, {
        grant_type: 'client_credentials',
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET
    });
    
    if (response.data.access_token) {
        return response.data.access_token;
    }
    throw new Error('Failed to retrieve access token');
}

// Usage
// const token = await getAccessToken();

Implementation

Setting a wrap-up code is not a single atomic action in all contexts. It generally involves two distinct steps:

  1. Identify the Conversation: Locate the specific conversation ID and verify its state is completed or closed.
  2. Apply the Wrap-Up: Send a PATCH request to the conversation endpoint with the wrap-up code ID.

Step 1: Retrieve Available Wrap-Up Codes

Before setting a wrap-up, you must know the valid wrapUpCodeId values. Wrap-up codes are organizational or queue-specific. If you are applying a wrap-up to a Messaging conversation, you typically need the ID of the code associated with the queue the agent was assigned to, or a global code.

We will fetch the list of wrap-up codes to find the correct ID.

Python SDK Example

from genesyscloud.platform.client import PlatformClient

def get_wrap_up_codes(client: PlatformClient) -> list:
    """
    Retrieves all available wrap-up codes for the organization.
    """
    try:
        # Use the analytics or conversations API to list wrap-up codes
        # Note: The SDK method for listing wrap-up codes is often under 
        # client.conversations_api or client.analytics_api depending on version.
        # In recent SDK versions, it is: client.conversations_api.get_conversations_wrapupcodes()
        
        response = client.conversations_api.get_conversations_wrapupcodes()
        
        if response and response.body:
            return response.body.entities
        else:
            print("No wrap-up codes found.")
            return []
            
    except Exception as e:
        print(f"Error retrieving wrap-up codes: {e}")
        return []

# Example usage to find a specific code by name
def find_wrap_up_code_by_name(client: PlatformClient, target_name: str) -> str | None:
    codes = get_wrap_up_codes(client)
    for code in codes:
        if code.name == target_name:
            return code.id
    return None

Key Parameter Explanation

  • get_conversations_wrapupcodes(): Returns a WrapUpCodeEntityListing.
  • entities: A list of WrapUpCode objects. Each object contains id, name, description, and enabled.

Step 2: Locate the Completed Conversation

You must target a conversation that has already ended. In Genesys Cloud, a conversation is eligible for wrap-up when its state is completed. You can search for conversations using the GET /api/v2/conversations endpoint with query parameters.

Python SDK Example

from genesyscloud.platform.client import PlatformClient
from genesyscloud.conversations.api.conversations_api import ConversationsApi

def find_completed_conversation(client: PlatformClient, participant_id: str) -> str | None:
    """
    Finds the most recent completed conversation for a specific participant.
    
    Args:
        client: Authenticated PlatformClient
        participant_id: The ID of the user or external user involved in the chat.
        
    Returns:
        The conversation ID string, or None if not found.
    """
    try:
        # Query parameters
        # state=completed ensures we only get finished conversations
        # type=messaging filters for chat/messaging interactions
        # participantIds filters by the specific user
        
        response = client.conversations_api.get_conversations(
            state="completed",
            type="messaging",
            participant_ids=[participant_id],
            sort_by="lastUpdatedTime",
            sort_order="desc",
            page_size=1
        )
        
        if response.body and response.body.entities and len(response.body.entities) > 0:
            return response.body.entities[0].id
        else:
            print(f"No completed messaging conversation found for participant {participant_id}")
            return None
            
    except Exception as e:
        print(f"Error searching for conversations: {e}")
        return None

Important Constraints

  • State Check: You cannot set a wrap-up code on a conversation with state active, ringing, or queued. The API will return a 409 Conflict if the conversation is not in a terminal state.
  • Participant ID: Ensure the participant_id corresponds to an entry in the participants array of the conversation object. For external users, this is often the externalId or the id of the participant object within the conversation.

Step 3: Apply the Wrap-Up Code

Once you have the conversationId and the wrapUpCodeId, you send a PATCH request to /api/v2/conversations/{conversationId}. The body must include the wrapUpCodeId field.

Python SDK Example

from genesyscloud.platform.client import PlatformClient
from genesyscloud.model.wrap_up_code import WrapUpCode

def set_wrap_up_code(client: PlatformClient, conversation_id: str, wrap_up_code_id: str) -> bool:
    """
    Applies a wrap-up code to a completed conversation.
    
    Args:
        client: Authenticated PlatformClient
        conversation_id: The ID of the conversation
        wrap_up_code_id: The ID of the wrap-up code to apply
        
    Returns:
        True if successful, False otherwise.
    """
    try:
        # Construct the body
        # The SDK may require a specific model class. 
        # For PATCH, we often just need the ID in a dict or a specific model.
        # In many SDK versions, the body is a dict or a Conversation object.
        
        body = {
            "wrapUpCodeId": wrap_up_code_id
        }
        
        # Execute the PATCH request
        # Note: The method name in the SDK is typically 
        # patch_conversations_conversation
        response = client.conversations_api.patch_conversations_conversation(
            conversation_id=conversation_id,
            body=body
        )
        
        print(f"Wrap-up code applied successfully. Status Code: {response.status_code}")
        return True
        
    except Exception as e:
        # Handle specific errors
        if hasattr(e, 'status_code') and e.status_code == 409:
            print("Conflict: Conversation is not in a state that allows wrap-up modification.")
        elif hasattr(e, 'status_code') and e.status_code == 404:
            print("Not Found: Conversation or Wrap-Up Code ID is invalid.")
        else:
            print(f"Error applying wrap-up code: {e}")
        return False

JavaScript (REST) Example

const axios = require('axios');

async function setWrapUpCodeREST(token, conversationId, wrapUpCodeId) {
    const url = `${GENESYS_BASE_URL}/api/v2/conversations/${conversationId}`;
    
    try {
        const response = await axios.patch(url, {
            wrapUpCodeId: wrapUpCodeId
        }, {
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json'
            }
        });
        
        console.log(`Wrap-up code applied. Status: ${response.status}`);
        return true;
    } catch (error) {
        if (error.response) {
            if (error.response.status === 409) {
                console.error("Conflict: Conversation state does not allow wrap-up changes.");
            } else if (error.response.status === 404) {
                console.error("Not Found: Invalid Conversation or Wrap-Up Code ID.");
            } else {
                console.error(`Error: ${error.response.status} - ${error.response.data}`);
            }
        } else {
            console.error("Network Error:", error.message);
        }
        return false;
    }
}

Step 4: Verification

After setting the wrap-up, it is good practice to verify the change. Retrieve the conversation again and check the wrapUpCodeId field in the response.

def verify_wrap_up(client: PlatformClient, conversation_id: str) -> str | None:
    """
    Retrieves the conversation and returns the applied wrap-up code ID.
    """
    try:
        response = client.conversations_api.get_conversations_conversation(
            conversation_id=conversation_id
        )
        
        if response.body:
            return response.body.wrap_up_code_id
        return None
    except Exception as e:
        print(f"Error verifying wrap-up: {e}")
        return None

Complete Working Example

This Python script combines all steps: authenticates, finds a wrap-up code by name, finds a recent completed conversation for a user, applies the code, and verifies the result.

import sys
import os
from genesyscloud.platform.client import PlatformClient

# Configuration
CLIENT_ID = os.getenv("GENESYS_CLIENT_ID")
CLIENT_SECRET = os.getenv("GENESYS_CLIENT_SECRET")
ENVIRONMENT = os.getenv("GENESYS_ENV", "mypurecloud.com")
PARTICIPANT_ID = os.getenv("GENESYS_PARTICIPANT_ID") # ID of the user/external user
TARGET_WRAP_UP_NAME = os.getenv("TARGET_WRAP_UP_NAME", "General")

def main():
    if not CLIENT_ID or not CLIENT_SECRET or not PARTICIPANT_ID:
        print("Error: Missing required environment variables.")
        print("Set: GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, GENESYS_PARTICIPANT_ID")
        sys.exit(1)

    # 1. Initialize Client
    print("Initializing Genesys Cloud Client...")
    client = PlatformClient(base_url=f"https://{ENVIRONMENT}")
    client.login(client_id=CLIENT_ID, client_secret=CLIENT_SECRET)

    # 2. Find Wrap-Up Code ID
    print(f"Searching for wrap-up code: '{TARGET_WRAP_UP_NAME}'")
    wrap_up_code_id = find_wrap_up_code_by_name(client, TARGET_WRAP_UP_NAME)
    
    if not wrap_up_code_id:
        print(f"Error: Wrap-up code '{TARGET_WRAP_UP_NAME}' not found.")
        sys.exit(1)
    
    print(f"Found Wrap-Up Code ID: {wrap_up_code_id}")

    # 3. Find Completed Conversation
    print(f"Searching for completed conversation for participant: {PARTICIPANT_ID}")
    conversation_id = find_completed_conversation(client, PARTICIPANT_ID)
    
    if not conversation_id:
        print("Error: No completed conversation found.")
        sys.exit(1)
    
    print(f"Found Conversation ID: {conversation_id}")

    # 4. Apply Wrap-Up Code
    print("Applying wrap-up code...")
    success = set_wrap_up_code(client, conversation_id, wrap_up_code_id)
    
    if not success:
        print("Failed to apply wrap-up code.")
        sys.exit(1)

    # 5. Verify
    print("Verifying wrap-up code...")
    applied_id = verify_wrap_up(client, conversation_id)
    
    if applied_id == wrap_up_code_id:
        print("Success: Wrap-up code applied and verified.")
    else:
        print(f"Warning: Verification mismatch. Expected {wrap_up_code_id}, got {applied_id}")

# Helper functions from previous steps included here for completeness

def find_wrap_up_code_by_name(client, target_name):
    try:
        response = client.conversations_api.get_conversations_wrapupcodes()
        if response and response.body:
            for code in response.body.entities:
                if code.name == target_name:
                    return code.id
    except Exception as e:
        print(f"Error listing wrap-up codes: {e}")
    return None

def find_completed_conversation(client, participant_id):
    try:
        response = client.conversations_api.get_conversations(
            state="completed",
            type="messaging",
            participant_ids=[participant_id],
            sort_by="lastUpdatedTime",
            sort_order="desc",
            page_size=1
        )
        if response.body and response.body.entities and len(response.body.entities) > 0:
            return response.body.entities[0].id
    except Exception as e:
        print(f"Error searching conversations: {e}")
    return None

def set_wrap_up_code(client, conversation_id, wrap_up_code_id):
    try:
        body = {"wrapUpCodeId": wrap_up_code_id}
        client.conversations_api.patch_conversations_conversation(
            conversation_id=conversation_id,
            body=body
        )
        return True
    except Exception as e:
        print(f"Error setting wrap-up: {e}")
        return False

def verify_wrap_up(client, conversation_id):
    try:
        response = client.conversations_api.get_conversations_conversation(
            conversation_id=conversation_id
        )
        if response.body:
            return response.body.wrap_up_code_id
    except Exception as e:
        print(f"Error verifying: {e}")
    return None

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 409 Conflict

Cause: The conversation is not in a state that allows modification. Specifically, the conversation state is not completed.

Fix:

  1. Check the conversation state using GET /api/v2/conversations/{id}.
  2. Ensure the conversation has ended. For messaging, this means the agent has closed the session.
  3. If the conversation is still active, wait for it to close or trigger the close action programmatically via the messaging API before attempting to set the wrap-up.
# Debugging code
response = client.conversations_api.get_conversations_conversation(conversation_id)
print(f"Current State: {response.body.state}")
if response.body.state != "completed":
    print("Conversation is not completed. Cannot set wrap-up.")

Error: 404 Not Found

Cause: The conversationId or wrapUpCodeId is invalid.

Fix:

  1. Verify the conversationId exists and belongs to the organization.
  2. Verify the wrapUpCodeId exists and is enabled.
  3. Check for typos in the IDs.

Error: 403 Forbidden

Cause: The OAuth token lacks the conversation:write scope.

Fix:

  1. Update the OAuth Client in the Genesys Cloud Admin Console.
  2. Add the conversation:write scope.
  3. Re-generate the token or refresh the authentication session.

Error: 429 Too Many Requests

Cause: The API rate limit has been exceeded.

Fix:

  1. Implement exponential backoff in your retry logic.
  2. Check the Retry-After header in the response.
import time

def retry_with_backoff(func, *args, max_retries=3, **kwargs):
    for attempt in range(max_retries):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            if hasattr(e, 'status_code') and e.status_code == 429:
                wait_time = 2 ** attempt
                print(f"Rate limited. Waiting {wait_time} seconds...")
                time.sleep(wait_time)
            else:
                raise e
    raise Exception("Max retries exceeded")

Official References