Sending Canned Responses in Genesys Cloud Chat via the Conversations API
What You Will Build
- A Python script that sends a predefined canned response to an active chat conversation using the Genesys Cloud Conversations API.
- This uses the
POST /api/v2/conversations/{conversationId}/eventsendpoint with a specific event type. - The code is implemented in Python using the
genesys-cloud-py-clientSDK.
Prerequisites
- OAuth Client Type: Service Account or Private Key.
- Required Scopes:
conversation:send(To send messages to conversations)conversation:read(To retrieve conversation details if needed for context)
- SDK Version:
genesys-cloud-py-clientv1.2.0 or later. - Language/Runtime: Python 3.8+.
- External Dependencies:
genesys-cloud-py-clientpython-dotenv(for secure credential management)
Authentication Setup
Genesys Cloud uses OAuth 2.0 for authentication. For server-side integrations like this one, you will typically use a Service Account with a Private Key or Client Credentials grant. The genesys-cloud-py-client handles token acquisition and refresh automatically when initialized correctly.
Create a .env file in your project root to store your credentials securely:
GENESYS_OAUTH_CLIENT_ID=your_client_id
GENESYS_OAUTH_CLIENT_SECRET=your_client_secret
GENESYS_OAUTH_PRIVATE_KEY=-----BEGIN RSA PRIVATE KEY-----
MIIE...
-----END RSA PRIVATE KEY-----
Initialize the client in your code. The SDK requires the private key to be passed as a string.
import os
from dotenv import load_dotenv
from purecloudplatformclientv2 import (
Configuration,
ApiClient,
ConversationApi,
ConversationEvent
)
# Load environment variables
load_dotenv()
def get_conversation_api() -> ConversationApi:
"""
Initializes and returns an authenticated ConversationApi instance.
"""
try:
# Load private key from environment variable
private_key = os.getenv("GENESYS_OAUTH_PRIVATE_KEY")
client_id = os.getenv("GENESYS_OAUTH_CLIENT_ID")
client_secret = os.getenv("GENESYS_OAUTH_CLIENT_SECRET")
if not private_key or not client_id:
raise ValueError("Missing required OAuth credentials in environment variables.")
# Configure the SDK
configuration = Configuration(
client_id=client_id,
client_secret=client_secret,
private_key=private_key
)
# Initialize API Client
api_client = ApiClient(configuration)
# Initialize Conversation API
conversation_api = ConversationApi(api_client)
return conversation_api
except Exception as e:
print(f"Authentication failed: {e}")
raise
# Get the API client instance
conversation_api = get_conversation_api()
Implementation
Step 1: Identify the Conversation and Canned Response
Before sending a message, you must know the conversationId of the active chat session. In a production environment, this ID is typically received via Web Messaging callbacks or from the Async API. For this tutorial, we assume you have the conversationId.
Additionally, you need the id of the canned response you wish to send. Canned responses are organizational assets. You can list them using the GET /api/v2/users/me/cannedresponses endpoint, but for this example, we will assume you have hardcoded the canned_response_id for brevity.
# Example IDs - Replace with actual values from your environment
CONVERSATION_ID = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
CANNED_RESPONSE_ID = "x9y8z7w6-v5u4-3210-fedc-ba0987654321"
Step 2: Construct the Conversation Event
To send a message via the Conversations API, you do not call a specific “send message” endpoint. Instead, you post an Event to the conversation. The type of event determines the action.
For a standard text message, the event type is message.
For a canned response, you must include the cannedResponseId in the event body.
The ConversationEvent object requires:
type: Must be set to"message".from: The identity sending the message. For a bot or service account, this is usually a generic user or system identity. However, in many chat contexts, if you are acting on behalf of an agent, you might use the agent’s user ID. If you are a pure backend service, you may need to ensure the service account has permission to act as a participant. Note: Standard canned responses are often tied to a specific user’s library. If the canned response belongs to an agent, the sender must be that agent or the system must have permissions to use it.
Here is how to construct the payload:
from purecloudplatformv2 import ConversationEvent
def create_canned_response_event(conversation_id: str, canned_response_id: str) -> ConversationEvent:
"""
Creates a ConversationEvent object for sending a canned response.
"""
event = ConversationEvent(
type="message",
canned_response_id=canned_response_id
)
return event
Note: In some complex scenarios, you might need to specify the from object explicitly if the default system identity is not allowed to send canned responses. However, the standard behavior for POST /api/v2/conversations/{conversationId}/events with type=message and canned_response_id allows the API to resolve the content.
Step 3: Send the Event and Handle Errors
Now, we invoke the post_conversation_event method. This method is asynchronous in nature but the Python SDK provides a synchronous wrapper by default. We must handle potential HTTP errors, specifically 400 Bad Request (invalid event structure), 401 Unauthorized (missing scopes), and 404 Not Found (invalid conversation ID).
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def send_canned_response(conversation_api: ConversationApi, conversation_id: str, canned_response_id: str) -> dict:
"""
Sends a canned response to a specific conversation.
Args:
conversation_api: The authenticated ConversationApi instance.
conversation_id: The unique identifier of the chat conversation.
canned_response_id: The unique identifier of the canned response.
Returns:
The response object from the API.
"""
try:
# Create the event payload
event = create_canned_response_event(conversation_id, canned_response_id)
logger.info(f"Sending canned response {canned_response_id} to conversation {conversation_id}")
# Execute the API call
response = conversation_api.post_conversation_event(
conversation_id=conversation_id,
body=event
)
logger.info(f"Message sent successfully. Event ID: {response.id}")
return response
except Exception as e:
# The SDK raises an exception for non-2xx responses
logger.error(f"Failed to send canned response: {e}")
# Check for specific HTTP errors if needed
if hasattr(e, 'status') and e.status == 401:
logger.error("Authentication failed. Check OAuth scopes and credentials.")
elif hasattr(e, 'status') and e.status == 403:
logger.error("Forbidden. The user/service account may not have access to this canned response.")
elif hasattr(e, 'status') and e.status == 404:
logger.error(f"Conversation {conversation_id} not found.")
else:
logger.error(f"Unexpected error: {e}")
raise e
Step 4: Processing Results
The response from post_conversation_event contains the id of the event that was created. This ID can be used for audit logs or to correlate subsequent messages. The response body typically looks like this:
{
"id": "event-uuid-1234",
"type": "message",
"timestamp": "2023-10-27T10:00:00.000Z",
"from": {
"id": "system-user-id",
"name": "System"
},
"cannedResponseId": "x9y8z7w6-v5u4-3210-fedc-ba0987654321"
}
Complete Working Example
Below is the full, copy-pasteable Python script. It combines authentication, payload construction, and error handling into a single executable module.
import os
import sys
import logging
from dotenv import load_dotenv
from purecloudplatformclientv2 import (
Configuration,
ApiClient,
ConversationApi,
ConversationEvent
)
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def initialize_client() -> ConversationApi:
"""
Initializes the Genesys Cloud API client using environment variables.
"""
load_dotenv()
client_id = os.getenv("GENESYS_OAUTH_CLIENT_ID")
client_secret = os.getenv("GENESYS_OAUTH_CLIENT_SECRET")
private_key = os.getenv("GENESYS_OAUTH_PRIVATE_KEY")
if not all([client_id, client_secret, private_key]):
raise ValueError("Missing GENESYS_OAUTH_CLIENT_ID, GENESYS_OAUTH_CLIENT_SECRET, or GENESYS_OAUTH_PRIVATE_KEY in .env")
try:
configuration = Configuration(
client_id=client_id,
client_secret=client_secret,
private_key=private_key
)
api_client = ApiClient(configuration)
return ConversationApi(api_client)
except Exception as e:
logger.error(f"Failed to initialize API client: {e}")
raise
def send_canned_response_to_chat(conversation_id: str, canned_response_id: str) -> None:
"""
Main function to send a canned response to a chat conversation.
"""
conversation_api = initialize_client()
try:
# 1. Construct the Event
# The type must be 'message' for chat interactions
event = ConversationEvent(
type="message",
canned_response_id=canned_response_id
)
logger.info(f"Attempting to send canned response '{canned_response_id}' to conversation '{conversation_id}'")
# 2. Send the Event
response = conversation_api.post_conversation_event(
conversation_id=conversation_id,
body=event
)
logger.info(f"Success! Event ID: {response.id}")
except Exception as e:
logger.error(f"Error sending canned response: {e}")
# Specific error handling for debugging
if hasattr(e, 'status'):
status_code = e.status
if status_code == 400:
logger.error("Bad Request: Check the conversation ID format or event structure.")
elif status_code == 401:
logger.error("Unauthorized: Check your OAuth credentials.")
elif status_code == 403:
logger.error("Forbidden: The service account may lack 'conversation:send' scope or access to the canned response.")
elif status_code == 404:
logger.error("Not Found: The conversation ID does not exist.")
sys.exit(1)
if __name__ == "__main__":
# Replace these with actual IDs from your Genesys Cloud instance
TARGET_CONVERSATION_ID = os.getenv("TARGET_CONVERSATION_ID", "provide-conversation-id")
TARGET_CANNED_RESPONSE_ID = os.getenv("TARGET_CANNED_RESPONSE_ID", "provide-canned-response-id")
if TARGET_CONVERSATION_ID == "provide-conversation-id" or TARGET_CANNED_RESPONSE_ID == "provide-canned-response-id":
print("Please set TARGET_CONVERSATION_ID and TARGET_CANNED_RESPONSE_ID in your .env file or update the script.")
sys.exit(1)
send_canned_response_to_chat(TARGET_CONVERSATION_ID, TARGET_CANNED_RESPONSE_ID)
Common Errors & Debugging
Error: 403 Forbidden - “User not allowed to send message”
Cause: The Service Account or User associated with the OAuth token does not have the conversation:send scope, or the canned response is owned by a different user who has restricted sharing.
Fix:
- Verify the OAuth token has the
conversation:sendscope. - Ensure the canned response is shared with the organization or the specific user/service account.
- If using a Service Account, ensure it is configured to act as a participant in conversations.
Error: 400 Bad Request - “Invalid event type”
Cause: The type field in the ConversationEvent was not set to "message".
Fix: Ensure event.type = "message" is explicitly set. Other types like transfer or hangup are handled differently.
Error: 404 Not Found - “Conversation not found”
Cause: The conversationId provided is invalid or the conversation has ended and been archived/purged.
Fix: Verify the conversationId is active. You can check the conversation status via GET /api/v2/conversations/{conversationId}. If the conversation is closed, you cannot send new messages.