How to Call a Shared Flow from Multiple Inbound Call Flows in Genesys Cloud CX
What You Will Build
- This tutorial demonstrates how to configure a reusable “Common Module” (a Shared Flow) and invoke it from multiple distinct inbound call flows using the Genesys Cloud CX API.
- The solution uses the Genesys Cloud CX Flow API (
/api/v2/fm/flows) and the Python SDK (genesys-cloud-sdk-python) to programmatically create a shared flow and update existing flows to reference it. - The programming language used is Python 3.9+.
Prerequisites
- OAuth Client: A Genesys Cloud CX OAuth Client with the
privatetype. - Required Scopes:
flow:flow:read(to inspect existing flows)flow:flow:write(to create or update flows)flow:flow:delete(optional, for cleanup)organization:read(to resolve organization ID if not hardcoded)
- SDK Version:
genesys-cloud-sdk-pythonversion 139.0.0 or later. - Runtime: Python 3.9 or later.
- Dependencies:
pip install genesys-cloud-sdk-python
Authentication Setup
Genesys Cloud CX uses OAuth 2.0 for API authentication. The most robust method for server-to-server integration is the Client Credentials flow. The SDK handles token caching and refresh automatically if configured correctly.
import os
import sys
from purecloud_platform_client import Configuration, PureCloudAuthFlow, ApiClient, FlowApi
def get_purecloud_api_client() -> ApiClient:
"""
Initialize and return a configured PureCloud API Client.
Uses environment variables for credentials.
"""
# Retrieve credentials from environment variables
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
env_url = os.getenv("GENESYS_ENV_URL", "https://api.mypurecloud.com")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET environment variables are required.")
# Configure the SDK client
config = Configuration(
host=env_url,
client_id=client_id,
client_secret=client_secret,
oauth_client_type="private",
oauth_scopes=[
"flow:flow:read",
"flow:flow:write",
"flow:flow:delete"
]
)
# Create the API client instance
# The SDK manages the OAuth token lifecycle automatically
api_client = ApiClient(configuration=config)
return api_client
Implementation
Step 1: Create the Shared “Common Module” Flow
In Genesys Cloud CX, a “Common Module” is technically a Flow with the type set to common. This flow does not have an endpoint itself; it is designed to be called by other flows.
We will create a simple shared flow that performs two actions:
- Adds a tag
common-module-executedto the interaction. - Plays a brief audio message (using a standard system prompt or a custom recording ID).
Note: For this example, we will use a generic PlayPrompt action with a placeholder prompt ID. In production, you would replace PROMPT_ID with a valid prompt ID from your organization.
from purecloud_platform_client import FlowApi, Flow, FlowAction, FlowPlayPromptAction, FlowTagInteractionAction
from purecloud_platform_client import FlowPromptRef, FlowPromptTextRef
def create_shared_flow(api_client: ApiClient, org_id: str) -> str:
"""
Creates a Common Type flow that tags the interaction.
Returns the Flow ID.
"""
flow_api = FlowApi(api_client)
# Define the actions for the shared flow
# Action 1: Tag the interaction
tag_action = FlowTagInteractionAction(
name="TagInteraction",
tags=["common-module-executed", "shared-flow-test"]
)
# Action 2: Play a prompt (Text-to-Speech for simplicity)
# In a real scenario, you might use a recording ID
play_prompt_action = FlowPlayPromptAction(
name="PlaySharedMessage",
prompt=FlowPromptTextRef(
text="This is a message from the shared common module.",
locale="en-US"
)
)
# Define the flow structure
# A common flow typically has a single entry point and a sequence of actions
flow_definition = Flow(
name="Shared Common Module - Tag and Greet",
description="A reusable common module that tags interactions and plays a greeting.",
type="common", # CRITICAL: Sets the flow type to Common Module
active=True,
actions=[
tag_action,
play_prompt_action
],
# Common flows do not have endpoints, but they do have a definition structure
# The SDK handles the internal node linking for simple linear flows
)
try:
# Post the flow
# Note: The SDK post_flow method requires a Flow object
created_flow = flow_api.post_flow(body=flow_definition)
print(f"Successfully created shared flow with ID: {created_flow.id}")
return created_flow.id
except Exception as e:
print(f"Error creating shared flow: {e}")
raise
Step 2: Identify Inbound Flows to Update
We need to find existing inbound call flows to which we will add the call to the shared module. In a production script, you would filter by name, tags, or specific criteria. For this tutorial, we will retrieve all active inbound flows and select the first one for demonstration.
def get_active_inbound_flows(api_client: ApiClient, limit: int = 10) -> list:
"""
Retrieves a list of active inbound flows.
"""
flow_api = FlowApi(api_client)
try:
# Query for active inbound flows
# The API returns a paged response
response = flow_api.get_flows(
type="inbound",
active="true",
page_size=limit,
page_number=1
)
if response.entities:
return response.entities
else:
print("No active inbound flows found.")
return []
except Exception as e:
print(f"Error retrieving inbound flows: {e}")
return []
Step 3: Update Inbound Flow to Call the Shared Module
This is the core integration step. We must modify an existing inbound flow to include a CallFlow action that points to the shared flow ID created in Step 1.
Important: Modifying a flow requires updating the entire flow definition. You cannot patch individual actions via the REST API directly in all SDK versions; it is safer to retrieve the flow, modify the object in memory, and post the updated object back.
Logic:
- Retrieve the inbound flow.
- Create a
CallFlowaction referencing the shared flow ID. - Insert this action at the beginning of the flow’s action list (or at a specific node if the flow has complex branching).
- Post the updated flow back to Genesys.
from purecloud_platform_client import FlowCallFlowAction
def add_shared_flow_call_to_inbound_flow(api_client: ApiClient, inbound_flow_id: str, shared_flow_id: str) -> bool:
"""
Updates an inbound flow to call the shared common module at the start.
"""
flow_api = FlowApi(api_client)
try:
# 1. Get the existing inbound flow
existing_flow = flow_api.get_flow(flow_id=inbound_flow_id)
# 2. Create the CallFlow action
call_shared_action = FlowCallFlowAction(
name="CallSharedCommonModule",
flow=FlowPromptRef( # Note: SDK typing for flow ref often uses PromptRef structure for ID/Version
id=shared_flow_id,
version=existing_flow.version # It is best practice to use the current version of the target flow if known, or None for latest
)
)
# 3. Insert the new action at the beginning of the list
# Note: This assumes a linear flow structure for simplicity.
# In complex flows with nodes/edges, you would need to manipulate the 'nodes' and 'edges' arrays.
# For this tutorial, we assume the 'actions' list is used (simplified linear flow).
if not hasattr(existing_flow, 'actions') or existing_flow.actions is None:
print("Warning: Inbound flow does not have a simple actions list. Skipping update.")
return False
# Prepend the call action
existing_flow.actions.insert(0, call_shared_action)
# 4. Post the updated flow
# The SDK handles the PUT request with the updated version
updated_flow = flow_api.put_flow(
flow_id=inbound_flow_id,
body=existing_flow
)
print(f"Successfully updated inbound flow '{updated_flow.name}' to call shared flow.")
return True
except Exception as e:
print(f"Error updating inbound flow {inbound_flow_id}: {e}")
return False
Complete Working Example
The following script combines all steps. It authenticates, creates a shared common module, finds an active inbound flow, and updates that inbound flow to call the shared module.
import os
import sys
from purecloud_platform_client import Configuration, ApiClient, FlowApi
from purecloud_platform_client import Flow, FlowAction, FlowPlayPromptAction, FlowTagInteractionAction
from purecloud_platform_client import FlowPromptTextRef, FlowCallFlowAction, FlowPromptRef
def get_purecloud_api_client() -> ApiClient:
"""Initialize and return a configured PureCloud API Client."""
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
env_url = os.getenv("GENESYS_ENV_URL", "https://api.mypurecloud.com")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET environment variables are required.")
config = Configuration(
host=env_url,
client_id=client_id,
client_secret=client_secret,
oauth_client_type="private",
oauth_scopes=[
"flow:flow:read",
"flow:flow:write"
]
)
return ApiClient(configuration=config)
def create_shared_flow(api_client: ApiClient) -> str:
"""Creates a Common Type flow that tags the interaction."""
flow_api = FlowApi(api_client)
# Define actions
tag_action = FlowTagInteractionAction(
name="TagInteraction",
tags=["common-module-executed"]
)
play_prompt_action = FlowPlayPromptAction(
name="PlaySharedMessage",
prompt=FlowPromptTextRef(
text="This is a message from the shared common module.",
locale="en-US"
)
)
# Define flow
flow_definition = Flow(
name="Shared Common Module - Tag and Greet",
description="A reusable common module.",
type="common",
active=True,
actions=[tag_action, play_prompt_action]
)
try:
created_flow = flow_api.post_flow(body=flow_definition)
return created_flow.id
except Exception as e:
print(f"Error creating shared flow: {e}")
sys.exit(1)
def get_first_active_inbound_flow(api_client: ApiClient) -> str:
"""Retrieves the ID of the first active inbound flow."""
flow_api = FlowApi(api_client)
try:
response = flow_api.get_flows(type="inbound", active="true", page_size=1)
if response.entities and len(response.entities) > 0:
return response.entities[0].id
else:
print("No active inbound flows found.")
sys.exit(1)
except Exception as e:
print(f"Error retrieving flows: {e}")
sys.exit(1)
def update_inbound_flow_with_shared_call(api_client: ApiClient, inbound_flow_id: str, shared_flow_id: str) -> None:
"""Updates an inbound flow to call the shared common module."""
flow_api = FlowApi(api_client)
try:
# Get existing flow
existing_flow = flow_api.get_flow(flow_id=inbound_flow_id)
# Create CallFlow action
call_shared_action = FlowCallFlowAction(
name="CallSharedCommonModule",
flow=FlowPromptRef(
id=shared_flow_id,
version=None # Use None to always call the latest active version
)
)
# Check if flow has actions list (linear flow assumption)
if not hasattr(existing_flow, 'actions') or existing_flow.actions is None:
print("This flow does not use a simple actions list. Manual node manipulation required.")
return
# Insert at the beginning
existing_flow.actions.insert(0, call_shared_action)
# Update flow
flow_api.put_flow(flow_id=inbound_flow_id, body=existing_flow)
print(f"Successfully updated flow '{existing_flow.name}' (ID: {inbound_flow_id})")
except Exception as e:
print(f"Error updating flow: {e}")
raise
def main():
# 1. Authenticate
print("Authenticating...")
api_client = get_purecloud_api_client()
# 2. Create Shared Flow
print("Creating shared common module...")
shared_flow_id = create_shared_flow(api_client)
print(f"Shared Flow ID: {shared_flow_id}")
# 3. Get Target Inbound Flow
print("Finding active inbound flow...")
inbound_flow_id = get_first_active_inbound_flow(api_client)
print(f"Target Inbound Flow ID: {inbound_flow_id}")
# 4. Update Inbound Flow
print("Updating inbound flow to call shared module...")
update_inbound_flow_with_shared_call(api_client, inbound_flow_id, shared_flow_id)
print("Done.")
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden or Insufficient Permissions
Cause: The OAuth client lacks the required scopes.
Fix: Ensure your OAuth client has flow:flow:write and flow:flow:read. If you are using the SDK, verify the oauth_scopes list in the Configuration object.
Code Check:
oauth_scopes=[
"flow:flow:read",
"flow:flow:write"
]
Error: 409 Conflict or Version Mismatch
Cause: When updating a flow via put_flow, the SDK sends the current version of the flow. If another user or script modified the flow between your get_flow and put_flow calls, the version number will be stale.
Fix: Implement a retry loop that re-fetches the flow before attempting the update again.
Code Fix:
import time
def update_with_retry(api_client, inbound_flow_id, shared_flow_id, max_retries=3):
for attempt in range(max_retries):
try:
update_inbound_flow_with_shared_call(api_client, inbound_flow_id, shared_flow_id)
return
except Exception as e:
if "version" in str(e).lower() and attempt < max_retries - 1:
print(f"Version conflict. Retrying ({attempt + 1}/{max_retries})...")
time.sleep(1)
continue
raise e
Error: 500 Internal Server Error or Invalid Flow Definition
Cause: The flow definition is structurally invalid. Common issues include:
- Missing
typefield (must becommonfor shared modules). - Referencing a non-existent prompt ID.
- Circular references in complex node-based flows.
Fix: Validate the JSON structure before posting. Use the Genesys Cloud CX Flow Builder UI to export a working flow as JSON to compare against your SDK-generated object. Ensure the FlowPlayPromptAction uses a valid FlowPromptRef or FlowPromptTextRef.
Error: CallFlow Action Not Executing
Cause: The inbound flow is not configured to wait for the common module to complete, or the common module has an error.
Fix:
- Check the Interaction Tags in the Genesys Cloud CX Admin Console. If
common-module-executedis present, the call succeeded. - If the tag is missing, check the Flow Diagnostics for the inbound flow.
- Ensure the common module is
active=True. If a common module is deactivated, calls to it may fail or be skipped depending on the inbound flow’s error handling configuration.