Export All Architect Flows as JSON Using Genesys Cloud CX as Code CLI
What You Will Build
- One sentence: You will write a Python script that leverages the Genesys Cloud CX as Code CLI library to authenticate, retrieve every Architect flow in your organization, and export them as structured JSON files.
- One sentence: This uses the
purecloud-platform-client-v2Python SDK, which is the underlying engine for the CX as Code CLI. - One sentence: The code is written in Python 3.9+, but the logic applies directly to any language supported by the Genesys Cloud SDKs.
Prerequisites
- OAuth Client Type: Confidential Client (Client Credentials Flow).
- Required Scopes:
architect:flow:readis mandatory to retrieve flow definitions. If you need to export related entities like IVR menus or call queues referenced in flows, addarchitect:ivr:readandarchitect:callqueue:read. - SDK Version:
purecloud-platform-client-v2version 130.0.0 or higher. - Language/Runtime: Python 3.9 or higher.
- External Dependencies:
purecloud-platform-client-v2python-dotenv(for secure credential management)os(standard library)json(standard library)logging(standard library)
Authentication Setup
Genesys Cloud APIs require OAuth 2.0 authentication. The Python SDK handles the token acquisition and refresh cycles automatically once initialized. You must provide a Client ID and Client Secret. Storing these in environment variables is the production-standard practice.
Create a .env file in your project root:
GENESYS_CLOUD_CLIENT_ID=your_client_id_here
GENESYS_CLOUD_CLIENT_SECRET=your_client_secret_here
GENESYS_CLOUD_REGION=us-east-1
The following code initializes the SDK platform client. This object is the gateway to all API calls.
import os
import logging
from purecloud_platform_client_v2 import PlatformClient, Configuration
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def init_platform_client() -> PlatformClient:
"""
Initializes and returns a configured PlatformClient instance.
"""
# Load environment variables
client_id = os.getenv("GENESYS_CLOUD_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLOUD_CLIENT_SECRET")
region = os.getenv("GENESYS_CLOUD_REGION", "us-east-1")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET must be set in environment variables.")
# Configure the platform client
platform_client = PlatformClient()
# Set the region (e.g., us-east-1, eu-west-1)
platform_client.set_region(region)
# Set the credentials
platform_client.set_credentials(client_id, client_secret)
logger.info(f"Platform client initialized for region: {region}")
return platform_client
Implementation
Step 1: Retrieve All Architect Flows with Pagination
The /api/v2/architect/flows endpoint returns a paginated list of flows. You cannot retrieve all flows in a single request if your organization has more than 1,000 flows (the default page size limit). You must handle pagination by checking the nextPage link or iterating through pages until total is reached.
The SDK provides a helper method get_architect_flows which handles the HTTP request, but you must manage the pagination loop.
from purecloud_platform_client_v2 import ArchitectApi
from purecloud_platform_client_v2.rest import ApiException
def get_all_flows(platform_client: PlatformClient) -> list:
"""
Retrieves all Architect flows from Genesys Cloud using pagination.
Args:
platform_client: The initialized PlatformClient instance.
Returns:
A list of Flow objects.
"""
api_instance = ArchitectApi(platform_client)
all_flows = []
# Initial request parameters
page_size = 100
page_number = 1
total_flows = 0
try:
while True:
logger.info(f"Fetching page {page_number} of flows...")
# Make the API call
# expand='none' is sufficient for exporting the flow definition itself.
# If you need embedded entities, use expand='all' or specific fields.
response = api_instance.get_architect_flows(
page_size=page_size,
page_number=page_number,
expand='none'
)
if response.entities is None:
logger.warning("No flows found in this page.")
break
all_flows.extend(response.entities)
# Update total count for logging
if total_flows == 0:
total_flows = response.total
logger.info(f"Retrieved {len(all_flows)} flows so far. Total available: {total_flows}")
# Check if there are more pages
if page_number * page_size >= total_flows:
break
page_number += 1
except ApiException as e:
logger.error(f"Exception when calling ArchitectApi->get_architect_flows: {e}")
if e.status == 401:
logger.error("Authentication failed. Check your Client ID and Secret.")
elif e.status == 403:
logger.error("Forbidden. Ensure the OAuth client has 'architect:flow:read' scope.")
elif e.status == 429:
logger.error("Rate limit exceeded. Implement retry logic with exponential backoff.")
raise e
return all_flows
Step 2: Export Flows to JSON Files
Once you have the list of Flow objects, you need to serialize them. The CX as Code CLI exports flows as JSON files named after the flow’s id or name. For safety and idempotency, using the id is recommended.
You must also handle special characters in flow names if you choose to name files by name. The Flow object contains the full definition, including actions, triggers, and settings.
import json
import os
from purecloud_platform_client_v2.models import Flow
def sanitize_filename(name: str) -> str:
"""
Removes or replaces characters that are invalid in filenames.
"""
# Replace problematic characters with underscores
invalid_chars = '<>:"/\\|?*'
sanitized = "".join([c if c not in invalid_chars else '_' for c in name])
# Limit filename length to 255 characters
return sanitized[:255]
def export_flows_to_json(flows: list, output_dir: str) -> None:
"""
Exports a list of Flow objects to individual JSON files.
Args:
flows: List of Flow objects retrieved from the API.
output_dir: Directory path where JSON files will be saved.
"""
if not os.path.exists(output_dir):
os.makedirs(output_dir)
logger.info(f"Created output directory: {output_dir}")
exported_count = 0
failed_count = 0
for flow in flows:
try:
# Use the flow ID for the filename to ensure uniqueness
filename = f"{flow.id}.json"
filepath = os.path.join(output_dir, filename)
# Convert the Flow object to a dictionary
# to_dict() is a method provided by the SDK models
flow_dict = flow.to_dict()
# Write to file with pretty printing
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(flow_dict, f, indent=2, ensure_ascii=False)
exported_count += 1
logger.debug(f"Exported flow: {flow.name} ({flow.id})")
except Exception as e:
failed_count += 1
logger.error(f"Failed to export flow {flow.name} ({flow.id}): {e}")
logger.info(f"Export complete. Successfully exported: {exported_count}, Failed: {failed_count}")
Step 3: Handling Related Entities (Optional but Recommended)
Architect flows often reference other entities like IVR Menus, Call Queues, or Email Routes. If you want a fully portable export (i.e., you can import this JSON into another Genesys Cloud org), you must export these dependencies as well.
For this tutorial, we will focus on IVR Menus. The process is identical to flows: paginate through /api/v2/architect/ivrmenus and export to JSON.
from purecloud_platform_client_v2 import ArchitectApi
from purecloud_platform_client_v2.rest import ApiException
def get_all_ivr_menus(platform_client: PlatformClient) -> list:
"""
Retrieves all IVR Menus from Genesys Cloud.
"""
api_instance = ArchitectApi(platform_client)
all_menus = []
page_size = 100
page_number = 1
total_menus = 0
try:
while True:
response = api_instance.get_architect_ivrmenus(
page_size=page_size,
page_number=page_number,
expand='none'
)
if response.entities is None:
break
all_menus.extend(response.entities)
if total_menus == 0:
total_menus = response.total
if page_number * page_size >= total_menus:
break
page_number += 1
except ApiException as e:
logger.error(f"Exception when calling ArchitectApi->get_architect_ivrmenus: {e}")
raise e
return all_menus
def export_ivr_menus_to_json(menus: list, output_dir: str) -> None:
"""
Exports IVR Menus to JSON files.
"""
ivr_dir = os.path.join(output_dir, "ivr_menus")
if not os.path.exists(ivr_dir):
os.makedirs(ivr_dir)
for menu in menus:
try:
filename = f"{menu.id}.json"
filepath = os.path.join(ivr_dir, filename)
menu_dict = menu.to_dict()
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(menu_dict, f, indent=2, ensure_ascii=False)
except Exception as e:
logger.error(f"Failed to export IVR Menu {menu.name}: {e}")
Complete Working Example
Below is the full, copy-pasteable Python script. Save this as export_architect_flows.py.
import os
import json
import logging
import sys
from purecloud_platform_client_v2 import PlatformClient, ArchitectApi
from purecloud_platform_client_v2.rest import ApiException
from purecloud_platform_client_v2.models import Flow
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler(sys.stdout)]
)
logger = logging.getLogger(__name__)
def init_platform_client() -> PlatformClient:
client_id = os.getenv("GENESYS_CLOUD_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLOUD_CLIENT_SECRET")
region = os.getenv("GENESYS_CLOUD_REGION", "us-east-1")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET must be set in environment variables.")
platform_client = PlatformClient()
platform_client.set_region(region)
platform_client.set_credentials(client_id, client_secret)
return platform_client
def get_all_flows(platform_client: PlatformClient) -> list:
api_instance = ArchitectApi(platform_client)
all_flows = []
page_size = 100
page_number = 1
total_flows = 0
try:
while True:
response = api_instance.get_architect_flows(
page_size=page_size,
page_number=page_number,
expand='none'
)
if response.entities is None:
break
all_flows.extend(response.entities)
if total_flows == 0:
total_flows = response.total
if page_number * page_size >= total_flows:
break
page_number += 1
except ApiException as e:
logger.error(f"Error fetching flows: {e}")
raise e
return all_flows
def export_flows_to_json(flows: list, output_dir: str) -> None:
if not os.path.exists(output_dir):
os.makedirs(output_dir)
exported_count = 0
for flow in flows:
try:
filename = f"{flow.id}.json"
filepath = os.path.join(output_dir, filename)
flow_dict = flow.to_dict()
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(flow_dict, f, indent=2, ensure_ascii=False)
exported_count += 1
except Exception as e:
logger.error(f"Failed to export flow {flow.id}: {e}")
logger.info(f"Exported {exported_count} flows to {output_dir}")
def main():
try:
logger.info("Initializing Platform Client...")
platform_client = init_platform_client()
logger.info("Fetching all Architect flows...")
flows = get_all_flows(platform_client)
logger.info(f"Total flows retrieved: {len(flows)}")
output_dir = "./exported_flows"
logger.info(f"Exporting flows to {output_dir}...")
export_flows_to_json(flows, output_dir)
logger.info("Export process completed successfully.")
except Exception as e:
logger.error(f"Fatal error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 401 Unauthorized
- What causes it: The Client ID or Client Secret is incorrect, or the OAuth token has expired (though the SDK usually handles refresh).
- How to fix it: Verify your credentials in the Genesys Cloud Admin Console under Administration > Security > OAuth Clients. Ensure you are using the correct credentials for the environment (Production vs. Sandbox).
- Code Fix: Check your
.envfile for typos.
Error: 403 Forbidden
- What causes it: The OAuth Client does not have the required scope.
- How to fix it: In the Genesys Cloud Admin Console, edit your OAuth Client. Add the scope
architect:flow:read. Save the changes. You may need to revoke and re-issue the client secret for changes to take effect immediately. - Code Fix: No code change required, but ensure you are using the updated secret.
Error: 429 Too Many Requests
- What causes it: You have exceeded the API rate limit for your organization. This is common when exporting thousands of flows or related entities.
- How to fix it: Implement exponential backoff. The SDK does not automatically retry 429s for bulk operations.
- Code Fix: Wrap the API call in a retry loop.
import time
def get_flows_with_retry(api_instance, page_size, page_number, max_retries=5):
for attempt in range(max_retries):
try:
return api_instance.get_architect_flows(page_size=page_size, page_number=page_number, expand='none')
except ApiException as e:
if e.status == 429:
wait_time = 2 ** attempt
logger.warning(f"Rate limited. Waiting {wait_time} seconds before retry...")
time.sleep(wait_time)
else:
raise e
raise Exception("Max retries exceeded for 429 error")
Error: JSON Serialization Error
- What causes it: The
to_dict()method may includeNonevalues or complex objects thatjson.dumphandles, but sometimes custom objects or circular references (rare in Genesys SDK) can cause issues. - How to fix it: The
purecloud-platform-client-v2SDK models are designed to be JSON-serializable viato_dict(). If you encounter issues, ensure you are using the latest SDK version. - Code Fix: Use
json.dump(flow.to_dict(), ...)as shown in the complete example.