Launching Genesys Cloud Architect Flows Programmatically
What You Will Build
- You will build a script that initiates a new conversation in Genesys Cloud and immediately routes it to a specified Architect flow using the
POST /api/v2/conversationsendpoint. - This tutorial uses the Genesys Cloud REST API v2 and the official Python SDK (
genesyscloud). - The primary programming language covered is Python, with HTTP request/response examples for clarity.
Prerequisites
- OAuth Client Type: A confidential client application registered in Genesys Cloud with the
client_credentialsgrant type. - Required Scopes:
conversation:write(to create the conversation)flow:write(if modifying flow configuration, though not strictly needed for simple execution initiation via conversation creation)user:read(optional, if you need to verify user details before launching)
- SDK Version:
genesyscloud>= 130.0.0 (latest stable). - Language/Runtime: Python 3.8+
- External Dependencies:
genesyscloud:pip install genesyscloudpython-dotenv:pip install python-dotenv(for secure credential management)
Authentication Setup
Genesys Cloud uses OAuth 2.0 for authentication. For server-to-server integrations like this one, the client_credentials flow is the standard. You must cache the access token and handle expiration (typically 1 hour) to avoid re-authenticating on every request.
The following code demonstrates a robust authentication helper using the official SDK, which handles token caching and refreshing automatically.
import os
from dotenv import load_dotenv
from platformclientv2 import AuthenticationClient
from platformclientv2.rest import ApiException
# Load environment variables
load_dotenv()
def get_auth_client():
"""
Initializes and returns an authenticated Genesys Cloud client.
"""
region = os.getenv("GENESYS_REGION", "us-east-1")
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET must be set in environment variables.")
# Initialize the authentication client
auth_client = AuthenticationClient(
region=region,
client_id=client_id,
client_secret=client_secret
)
# Authenticate with required scopes
try:
auth_client.authenticate(scope=["conversation:write", "flow:write"])
return auth_client
except ApiException as e:
print(f"Authentication failed: {e.status} {e.reason}")
raise
# Get the authenticated client
auth_client = get_auth_client()
Note: The AuthenticationClient in the Python SDK automatically manages the underlying requests session, handling 401 Unauthorized errors by refreshing the token if it has expired. This eliminates the need for manual token refresh logic in your application code.
Implementation
Step 1: Identify the Target Flow ID
To launch a flow, you need its unique identifier (UUID). You cannot launch a flow by name alone via the API. If you do not know the Flow ID, you can query it using the GET /api/v2/flows endpoint.
from platformclientv2 import FlowApi
def get_flow_id_by_name(auth_client: AuthenticationClient, flow_name: str) -> str:
"""
Retrieves the Flow ID for a given flow name.
"""
flow_api = FlowApi(auth_client.get_client())
try:
# List all flows (paginated)
flows_response = flow_api.get_flows(page_size=100)
if not flows_response.entities:
raise ValueError("No flows found in the organization.")
for flow in flows_response.entities:
if flow.name == flow_name:
print(f"Found Flow ID: {flow.id}")
return flow.id
raise ValueError(f"Flow with name '{flow_name}' not found.")
except ApiException as e:
print(f"Error fetching flows: {e.status} {e.reason}")
raise
# Example usage
# flow_id = get_flow_id_by_name(auth_client, "My Customer Support Flow")
Error Handling: If the flow is archived or inactive, it may not appear in the standard list or may fail validation during conversation creation. Ensure the flow status is Active in the Genesys Cloud Admin console.
Step 2: Construct the Conversation Creation Payload
The core mechanism for “launching” an Architect flow from an external app is to create a new conversation with a specific flowID embedded in the request body. This tells Genesys Cloud to route the new conversation instance to that flow immediately upon creation.
For this example, we will create a Chat conversation. You can adapt this for Voice, Email, or SMS by changing the type and providing appropriate media-specific details (e.g., phone numbers for Voice).
Required Parameters:
type: The channel type (e.g.,"chat","voice","email").flowId: The UUID of the Architect flow to execute.origin: The origin of the conversation (e.g.,"webchat").wrapUpCode: Optional, but recommended for initial state tracking.
from platformclientv2 import ConversationApi
from platformclientv2.rest import ApiException
import json
def launch_flow_via_conversation(auth_client: AuthenticationClient, flow_id: str, customer_name: str, customer_email: str) -> str:
"""
Creates a new Chat conversation and routes it to the specified Architect Flow.
Args:
auth_client: The authenticated Genesys Cloud client.
flow_id: The UUID of the target Architect flow.
customer_name: Name of the external customer.
customer_email: Email of the external customer.
Returns:
The ID of the newly created conversation.
"""
conversation_api = ConversationApi(auth_client.get_client())
# Construct the conversation body
# Note: For Chat, we must provide initial messages or participant details.
# The flowId in the 'routing' object is the key to triggering the flow.
conversation_body = {
"type": "chat",
"wrapUpCode": "General",
"routing": {
"flowId": flow_id
},
"participants": [
{
"id": "external", # Placeholder for external user
"name": customer_name,
"email": customer_email,
"role": "customer"
}
],
"messages": [
{
"text": "Hello, I need assistance.",
"authorId": "external",
"authorName": customer_name
}
]
}
try:
# POST /api/v2/conversations
response = conversation_api.post_conversations(body=conversation_body)
print(f"Conversation created successfully. ID: {response.id}")
print(f"Conversation URI: {response.self_uri}")
return response.id
except ApiException as e:
# Detailed error handling for common issues
if e.status == 400:
print(f"Bad Request: Check flow_id validity and conversation body structure. Details: {e.body}")
elif e.status == 401:
print("Unauthorized: Token may have expired. Refresh authentication.")
elif e.status == 404:
print(f"Not Found: Flow ID '{flow_id}' does not exist or is not accessible.")
elif e.status == 429:
print("Rate Limited: Too many requests. Implement exponential backoff.")
else:
print(f"API Error: {e.status} {e.reason}")
raise
Step 3: Processing Results and Monitoring
Once the conversation is created, the Architect flow begins execution immediately. You can monitor the status of the flow execution by polling the conversation details or by subscribing to presence events. For immediate feedback, fetching the conversation details allows you to see if the flow has assigned an agent or dropped the conversation.
def get_conversation_details(auth_client: AuthenticationClient, conversation_id: str) -> dict:
"""
Retrieves detailed information about a specific conversation.
"""
conversation_api = ConversationApi(auth_client.get_client())
try:
response = conversation_api.get_conversation(conversation_id=conversation_id)
return {
"id": response.id,
"type": response.type,
"state": response.state,
"routing": {
"flowId": response.routing.flow_id if response.routing else None,
"queueId": response.routing.queue_id if response.routing else None,
"agentId": response.routing.agent_id if response.routing else None
}
}
except ApiException as e:
print(f"Error fetching conversation details: {e.status} {e.reason}")
return {}
Why this matters: The state field will indicate if the conversation is queued, connected, held, or closed. If routing.flowId is present but routing.agentId is null, the flow is likely still executing logic (e.g., waiting for IVR input in a voice flow or processing a chat node).
Complete Working Example
Below is a complete, runnable Python script that combines authentication, flow lookup, and flow execution. Save this as launch_flow.py.
import os
import sys
from dotenv import load_dotenv
from platformclientv2 import AuthenticationClient, FlowApi, ConversationApi
from platformclientv2.rest import ApiException
def load_env():
load_dotenv()
required_vars = ["GENESYS_REGION", "GENESYS_CLIENT_ID", "GENESYS_CLIENT_SECRET"]
for var in required_vars:
if not os.getenv(var):
raise EnvironmentError(f"Environment variable {var} is not set.")
def authenticate():
region = os.getenv("GENESYS_REGION", "us-east-1")
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
auth_client = AuthenticationClient(
region=region,
client_id=client_id,
client_secret=client_secret
)
try:
auth_client.authenticate(scope=["conversation:write", "flow:read"])
return auth_client
except ApiException as e:
print(f"Authentication failed: {e.status} {e.reason}")
sys.exit(1)
def find_flow(auth_client, flow_name):
flow_api = FlowApi(auth_client.get_client())
try:
flows = flow_api.get_flows(page_size=100).entities
for flow in flows:
if flow.name.lower() == flow_name.lower():
return flow.id
return None
except ApiException as e:
print(f"Error finding flow: {e.status} {e.reason}")
return None
def launch_flow(auth_client, flow_id, customer_name, customer_email):
conversation_api = ConversationApi(auth_client.get_client())
body = {
"type": "chat",
"wrapUpCode": "General",
"routing": {
"flowId": flow_id
},
"participants": [
{
"id": "external_user",
"name": customer_name,
"email": customer_email,
"role": "customer"
}
],
"messages": [
{
"text": "Initiating flow from external app.",
"authorId": "external_user",
"authorName": customer_name
}
]
}
try:
response = conversation_api.post_conversations(body=body)
print(f"SUCCESS: Flow launched. Conversation ID: {response.id}")
return response.id
except ApiException as e:
print(f"FAILED to launch flow: {e.status} {e.reason}")
if e.body:
print(f"Response Body: {e.body}")
return None
if __name__ == "__main__":
load_env()
# Configuration
TARGET_FLOW_NAME = "My Test Flow" # Replace with your actual flow name
CUSTOMER_NAME = "John Doe"
CUSTOMER_EMAIL = "john.doe@example.com"
# Step 1: Authenticate
print("Authenticating...")
auth_client = authenticate()
# Step 2: Find Flow ID
print(f"Searching for flow: {TARGET_FLOW_NAME}")
flow_id = find_flow(auth_client, TARGET_FLOW_NAME)
if not flow_id:
print(f"Error: Flow '{TARGET_FLOW_NAME}' not found. Please check the name.")
sys.exit(1)
print(f"Found Flow ID: {flow_id}")
# Step 3: Launch Flow
print("Launching flow...")
conv_id = launch_flow(auth_client, flow_id, CUSTOMER_NAME, CUSTOMER_EMAIL)
if conv_id:
print("Process completed successfully.")
else:
print("Process failed.")
sys.exit(1)
Common Errors & Debugging
Error: 400 Bad Request - “Invalid flow ID”
- Cause: The
flowIdprovided in theroutingobject is malformed, does not exist, or is not accessible by the OAuth client. - Fix: Verify the Flow ID using the
GET /api/v2/flows/{id}endpoint. Ensure the flow isActive. Ensure the OAuth client hasflow:readorflow:writescopes if the flow is restricted.
Error: 403 Forbidden - “Insufficient Permissions”
- Cause: The OAuth token lacks the
conversation:writescope. - Fix: Update your client credentials request to include
conversation:writein thescopeparameter.
Error: 429 Too Many Requests
- Cause: You have exceeded the API rate limit for your Genesys Cloud organization.
- Fix: Implement exponential backoff. Wait 1 second, then retry. If it fails again, wait 2 seconds, then 4 seconds, etc. The response header
Retry-Aftermay provide a specific wait time.
Error: 500 Internal Server Error
- Cause: A transient server-side error in Genesys Cloud.
- Fix: Retry the request after a short delay (1-5 seconds). If the error persists, check the Genesys Cloud Service Status page or contact Genesys Support.