Export All Architect Flows as JSON Using the Genesys Cloud CLI
What You Will Build
- A script that authenticates with Genesys Cloud and programmatically exports every Architect flow in your organization as a standalone JSON file.
- This tutorial uses the Genesys Cloud REST API via the Python SDK to retrieve flow definitions.
- The primary programming language is Python, with HTTP requests handled by the official
genesyscloudSDK.
Prerequisites
- OAuth Client: You need an OAuth Public Client or Confidential Client with the
adminrole or specific Architect permissions. - Required Scopes:
flow:flow:readis the minimum scope required to read flow definitions. - SDK Version:
genesyscloudPython SDK v2.0.0 or later. - Runtime: Python 3.8 or higher.
- External Dependencies:
genesyscloud(the official Genesys Cloud Python SDK)os(standard library for file handling)
Install the SDK via pip:
pip install genesyscloud
Authentication Setup
Genesys Cloud uses OAuth 2.0 for authentication. When using the Python SDK, you handle authentication by initializing the PureCloudPlatformClientV2 object. The SDK manages token refresh automatically if you provide the correct client credentials.
For this tutorial, we will use the Client Credentials Grant flow, which is suitable for server-to-server interactions like exporting data. You must configure your environment variables or use the SDK’s configuration file to store your client ID, client secret, and environment URL.
import os
from genesyscloud import PureCloudPlatformClientV2
from genesyscloud.api_exception import ApiException
def get_platform_client():
"""
Initialize and return the Genesys Cloud Platform Client.
Uses environment variables for credentials.
"""
# Load configuration from environment variables
client_id = os.environ.get('GENESYS_CLIENT_ID')
client_secret = os.environ.get('GENESYS_CLIENT_SECRET')
env_url = os.environ.get('GENESYS_ENV_URL', 'https://api.mypurecloud.com')
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 platform client
platform_client = PureCloudPlatformClientV2()
# Configure the client
platform_client.set_credentials(client_id, client_secret, env_url)
return platform_client
try:
client = get_platform_client()
print("Authentication successful.")
except Exception as e:
print(f"Authentication failed: {e}")
exit(1)
Key Note: The set_credentials method handles the initial token request. Subsequent API calls will automatically refresh the token if it expires. Ensure your client has the flow:flow:read scope assigned in the Genesys Cloud Admin Console under Integrations > OAuth > Clients.
Implementation
Step 1: Retrieve All Flow IDs
Architect flows are paginated resources. You cannot retrieve all flows in a single request if you have more than the default page size (usually 25 or 100 items). You must iterate through the pages to collect all flow IDs.
We will use the ApiFlows class from the SDK. The method get_flows() returns a FlowEntityListing object.
from genesyscloud.api.flows_api import ApiFlows
def get_all_flow_ids(client: PureCloudPlatformClientV2):
"""
Iterates through all pages of flows to collect every flow ID.
Returns a list of flow IDs.
"""
flows_api = ApiFlows(client)
flow_ids = []
# Initial request with a large page size to reduce round trips
# Max page size for this endpoint is typically 1000, but we use 200 for safety
page_size = 200
continuation_token = None
while True:
try:
# Call the API
response = flows_api.get_flows(page_size=page_size, continuation_token=continuation_token)
# Append IDs from the current page
if response.entities:
for flow in response.entities:
flow_ids.append(flow.id)
# Check if there are more pages
if not response.continuation_token:
break
# Update token for next iteration
continuation_token = response.continuation_token
except ApiError as e:
print(f"Error retrieving flows: {e}")
raise
return flow_ids
# Execute the retrieval
try:
all_flow_ids = get_all_flow_ids(client)
print(f"Found {len(all_flow_ids)} flows.")
except Exception as e:
print(f"Failed to retrieve flow IDs: {e}")
exit(1)
Why this approach?
The get_flows endpoint returns metadata (ID, name, description) but not the full flow definition. Retrieving the metadata first is efficient because it avoids downloading large JSON payloads for every flow until you are ready to export them.
Step 2: Export Each Flow as JSON
Now that you have the list of flow IDs, you need to fetch the full definition for each flow. The endpoint /api/v2/flows/{flowId} returns the complete flow structure.
We will create a function that:
- Fetches the flow definition.
- Converts it to a JSON string.
- Sanitizes the flow name to create a valid filename.
- Writes the JSON to a file in a local directory.
import json
import re
import os
from genesyscloud.api.flows_api import ApiFlows
from genesyscloud.api_exception import ApiError
def sanitize_filename(name: str) -> str:
"""
Removes invalid characters from filenames.
Replaces spaces and special chars with underscores.
"""
# Remove characters that are invalid in file names
sanitized = re.sub(r'[\\/*?:"<>|]', "", name)
# Replace spaces with underscores
sanitized = sanitized.replace(" ", "_")
# Truncate if too long
if len(sanitized) > 100:
sanitized = sanitized[:100]
return sanitized
def export_flows_to_json(client: PureCloudPlatformClientV2, flow_ids: list, output_dir: str):
"""
Downloads each flow definition and saves it as a JSON file.
"""
flows_api = ApiFlows(client)
# Create output directory if it does not exist
if not os.path.exists(output_dir):
os.makedirs(output_dir)
exported_count = 0
for flow_id in flow_ids:
try:
# Fetch the full flow definition
# Note: expand='all' is not typically needed for flows as the definition is included by default
response = flows_api.get_flow(flow_id=flow_id)
# Convert the response entity to a dictionary
# The SDK returns a Flow object. We can convert it to dict for JSON serialization.
flow_data = response.to_dict() if hasattr(response, 'to_dict') else response
# Sanitize the filename based on the flow name
flow_name = flow_data.get('name', 'unknown_flow')
filename = f"{sanitize_filename(flow_name)}.json"
filepath = os.path.join(output_dir, filename)
# Write to file
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(flow_data, f, indent=2)
exported_count += 1
print(f"Exported: {filename}")
except ApiError as e:
# Handle specific API errors
if e.status == 404:
print(f"Flow not found (deleted?): {flow_id}")
elif e.status == 429:
print(f"Rate limited. Skipping flow: {flow_id}")
else:
print(f"Error exporting flow {flow_id}: {e}")
except Exception as e:
print(f"Unexpected error exporting flow {flow_id}: {e}")
print(f"Successfully exported {exported_count} flows to {output_dir}")
Important Detail: The get_flow method returns a Flow object. Depending on the SDK version, you may need to call .to_dict() to serialize it properly into JSON. If you are using a newer version of the SDK that supports JSON serialization directly, you can pass the object to json.dumps. The code above assumes the standard pattern of converting to a dictionary first.
Step 3: Handle Rate Limiting and Retries
Genesys Cloud APIs enforce rate limits. If you have thousands of flows, you may hit a 429 Too Many Requests error. The SDK does not automatically retry all requests, so you should implement a basic retry mechanism or delay between requests.
Here is an enhanced version of the export loop with exponential backoff:
import time
def export_flows_with_retry(client: PureCloudPlatformClientV2, flow_ids: list, output_dir: str, max_retries: int = 3):
"""
Exports flows with retry logic for rate limiting.
"""
flows_api = ApiFlows(client)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
exported_count = 0
for flow_id in flow_ids:
retries = 0
success = False
while retries < max_retries and not success:
try:
response = flows_api.get_flow(flow_id=flow_id)
flow_data = response.to_dict() if hasattr(response, 'to_dict') else response
flow_name = flow_data.get('name', 'unknown_flow')
filename = f"{sanitize_filename(flow_name)}.json"
filepath = os.path.join(output_dir, filename)
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(flow_data, f, indent=2)
exported_count += 1
print(f"Exported: {filename}")
success = True
except ApiError as e:
if e.status == 429:
retries += 1
wait_time = 2 ** retries # Exponential backoff: 2s, 4s, 8s
print(f"Rate limited on flow {flow_id}. Retrying in {wait_time}s...")
time.sleep(wait_time)
else:
print(f"Error exporting flow {flow_id}: {e}")
success = True # Break the retry loop for non-429 errors
except Exception as e:
print(f"Unexpected error exporting flow {flow_id}: {e}")
success = True
print(f"Successfully exported {exported_count} flows to {output_dir}")
Complete Working Example
Below is the full, copy-pasteable script. Save this as export_flows.py.
import os
import json
import re
import time
from genesyscloud import PureCloudPlatformClientV2
from genesyscloud.api.flows_api import ApiFlows
from genesyscloud.api_exception import ApiError
def get_platform_client():
"""Initialize Genesys Cloud Platform Client."""
client_id = os.environ.get('GENESYS_CLIENT_ID')
client_secret = os.environ.get('GENESYS_CLIENT_SECRET')
env_url = os.environ.get('GENESYS_ENV_URL', 'https://api.mypurecloud.com')
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET must be set.")
platform_client = PureCloudPlatformClientV2()
platform_client.set_credentials(client_id, client_secret, env_url)
return platform_client
def sanitize_filename(name: str) -> str:
"""Sanitize flow name for use as a filename."""
sanitized = re.sub(r'[\\/*?:"<>|]', "", name)
sanitized = sanitized.replace(" ", "_")
if len(sanitized) > 100:
sanitized = sanitized[:100]
return sanitized
def get_all_flow_ids(client: PureCloudPlatformClientV2):
"""Retrieve all flow IDs from the organization."""
flows_api = ApiFlows(client)
flow_ids = []
page_size = 200
continuation_token = None
while True:
try:
response = flows_api.get_flows(page_size=page_size, continuation_token=continuation_token)
if response.entities:
for flow in response.entities:
flow_ids.append(flow.id)
if not response.continuation_token:
break
continuation_token = response.continuation_token
except ApiError as e:
print(f"Error retrieving flows: {e}")
raise
return flow_ids
def export_flows(client: PureCloudPlatformClientV2, flow_ids: list, output_dir: str):
"""Export each flow to a JSON file with retry logic."""
flows_api = ApiFlows(client)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
exported_count = 0
max_retries = 3
for flow_id in flow_ids:
retries = 0
success = False
while retries < max_retries and not success:
try:
response = flows_api.get_flow(flow_id=flow_id)
# Convert SDK object to dictionary
flow_data = response.to_dict() if hasattr(response, 'to_dict') else response
flow_name = flow_data.get('name', 'unknown_flow')
filename = f"{sanitize_filename(flow_name)}.json"
filepath = os.path.join(output_dir, filename)
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(flow_data, f, indent=2)
exported_count += 1
print(f"Exported: {filename}")
success = True
except ApiError as e:
if e.status == 429:
retries += 1
wait_time = 2 ** retries
print(f"Rate limited on flow {flow_id}. Retrying in {wait_time}s...")
time.sleep(wait_time)
else:
print(f"Error exporting flow {flow_id}: {e}")
success = True
except Exception as e:
print(f"Unexpected error exporting flow {flow_id}: {e}")
success = True
print(f"Successfully exported {exported_count} flows to {output_dir}")
if __name__ == "__main__":
try:
print("Initializing client...")
client = get_platform_client()
print("Retrieving all flow IDs...")
flow_ids = get_all_flow_ids(client)
print(f"Found {len(flow_ids)} flows.")
output_directory = "./exported_flows"
print(f"Exporting flows to {output_directory}...")
export_flows(client, flow_ids, output_directory)
except Exception as e:
print(f"Fatal error: {e}")
exit(1)
Common Errors & Debugging
Error: 401 Unauthorized
- Cause: The OAuth token is invalid, expired, or the client credentials are incorrect.
- Fix: Verify that
GENESYS_CLIENT_IDandGENESYS_CLIENT_SECRETare correct. Ensure the client is active in Genesys Cloud Admin. Check that theflow:flow:readscope is assigned to the client.
Error: 403 Forbidden
- Cause: The OAuth client lacks the required permissions.
- Fix: In Genesys Cloud Admin, go to Integrations > OAuth > Clients. Edit your client and ensure the Flow permissions include Read. Also, verify the user associated with the client (if using user impersonation) has Architect Read permissions.
Error: 429 Too Many Requests
- Cause: You are making API calls faster than the rate limit allows.
- Fix: The provided code includes exponential backoff. If you still encounter this, increase the
wait_timein the retry logic or add a fixed delay between requests.
Error: AttributeError: 'Flow' object has no attribute 'to_dict'
- Cause: Older versions of the Genesys Cloud Python SDK may not have the
to_dictmethod on all models. - Fix: Upgrade the SDK (
pip install --upgrade genesyscloud). If you cannot upgrade, you can manually serialize the object usingjson.dumps(response, default=str), but this may result in less clean JSON.