Increase Genesys Cloud Data Action Timeout via API
What You Will Build
- A script that identifies Data Actions with insufficient timeouts and updates them to handle longer-running integrations.
- This uses the Genesys Cloud PureCloud Platform Client V2 API.
- The tutorial covers Python and JavaScript implementations.
Prerequisites
- OAuth Client Type: Private Key Authentication (PKCE or Client Credentials are also supported, but Private Key is standard for server-side integrations).
- Required Scopes:
dataactions:readdataactions:write
- SDK Version:
- Python:
genesys-cloud-sdk-python>= 170.0.0 - JavaScript:
@genesyscloud/purecloud-platform-client-v2>= 170.0.0
- Python:
- Runtime Requirements:
- Python 3.8+
- Node.js 16+
- External Dependencies:
- Python:
pip install genesys-cloud-sdk-python - JavaScript:
npm install @genesyscloud/purecloud-platform-client-v2
- Python:
Authentication Setup
Genesys Cloud APIs require a valid OAuth 2.0 access token. For backend scripts that modify configuration, use Private Key authentication. This method does not require user interaction and allows for long-running token validity.
Python Authentication
import os
from purecloud_platform_client import Configuration, PlatformClient, PrivateKeyAuthenticator
def get_platform_client() -> PlatformClient:
"""
Initializes and returns an authenticated Genesys Cloud PlatformClient.
"""
config = Configuration()
# Load credentials from environment variables
# GENESYS_ORG_ID, GENESYS_CLIENT_ID, GENESYS_PRIVATE_KEY_FILE_PATH
config.set_property("org_id", os.getenv("GENESYS_ORG_ID"))
config.set_property("client_id", os.getenv("GENESYS_CLIENT_ID"))
config.set_property("private_key_file_path", os.getenv("GENESYS_PRIVATE_KEY_FILE_PATH"))
# Optional: Set region if not default (us-east-1)
# config.set_property("region", "eu-west-1")
# Initialize the authenticator
authenticator = PrivateKeyAuthenticator(config)
# Create the platform client
client = PlatformClient(authenticator)
# Verify connection by fetching a simple resource
try:
# This is a quick check to ensure auth works before heavy operations
client.get_user_me()
except Exception as e:
raise Exception(f"Authentication failed: {e}")
return client
JavaScript Authentication
const { PlatformClient, Configuration, PrivateKeyAuthenticator } = require('@genesyscloud/purecloud-platform-client-v2');
const fs = require('fs');
async function getPlatformClient() {
const config = new Configuration();
config.set_property('org_id', process.env.GENESYS_ORG_ID);
config.set_property('client_id', process.env.GENESYS_CLIENT_ID);
// The SDK expects the content of the PEM file for JS, or a path depending on version.
// For modern SDKs, passing the PEM content string is often more reliable in scripts.
const privateKey = fs.readFileSync(process.env.GENESYS_PRIVATE_KEY_PATH, 'utf8');
config.set_property('private_key', privateKey);
const authenticator = new PrivateKeyAuthenticator(config);
const client = new PlatformClient(authenticator);
try {
// Verify connection
await client.getUserMe();
} catch (error) {
throw new Error(`Authentication failed: ${error.message}`);
}
return client;
}
Implementation
Step 1: Identify Data Actions with Low Timeouts
The default timeout for a Data Action in Genesys Cloud is often 3 seconds (3000 ms). If your integration calls an external API that takes 5 seconds, the Data Action will fail with a timeout error before your external API responds. You must first locate these actions.
The API endpoint is GET /api/v2/dataactions.
OAuth Scope: dataactions:read
Python Implementation
from purecloud_platform_client.rest import ApiException
def find_slow_data_actions(client: PlatformClient) -> list:
"""
Retrieves all Data Actions and filters for those with timeout <= 3000ms.
"""
data_action_api = client.data_actions_api
actions_to_update = []
try:
# Fetch all data actions. The SDK handles pagination automatically if configured,
# but for simplicity, we fetch the first page.
# In production, iterate through 'next_page' if exists.
response = data_action_api.get_data_actions(page_size=100)
if not response.entities:
print("No Data Actions found.")
return []
for action in response.entities:
# Check if timeout is set and is less than or equal to 3 seconds (3000ms)
# Note: timeout is in milliseconds in the API model
if action.timeout and action.timeout <= 3000:
print(f"Found slow action: {action.name} (ID: {action.id}, Timeout: {action.timeout}ms)")
actions_to_update.append(action)
except ApiException as e:
if e.status == 401:
print("Authentication error: Token may be expired or invalid.")
elif e.status == 403:
print("Permission denied: Ensure 'dataactions:read' scope is granted.")
else:
print(f"API Error: {e}")
return actions_to_update
JavaScript Implementation
async function findSlowDataActions(client) {
const dataActionsApi = client.dataActionsApi;
const actionsToUpdate = [];
try {
// Fetch data actions
const response = await dataActionsApi.getDataActions({ pageSize: 100 });
if (!response.entities || response.entities.length === 0) {
console.log("No Data Actions found.");
return [];
}
for (const action of response.entities) {
// Check if timeout is set and is less than or equal to 3 seconds (3000ms)
if (action.timeout !== undefined && action.timeout <= 3000) {
console.log(`Found slow action: ${action.name} (ID: ${action.id}, Timeout: ${action.timeout}ms)`);
actionsToUpdate.push(action);
}
}
} catch (error) {
if (error.status === 401) {
console.error("Authentication error: Token may be expired or invalid.");
} else if (error.status === 403) {
console.error("Permission denied: Ensure 'dataactions:read' scope is granted.");
} else {
console.error(`API Error: ${error.message}`);
}
}
return actionsToUpdate;
}
Step 2: Increase the Timeout Limit
Once identified, you must update the Data Action entity. The maximum timeout allowed for a Data Action is 10 seconds (10000 ms). You cannot set it higher than this via the API. If your external call takes longer than 10 seconds, you must redesign the architecture (e.g., use async webhooks or Genesys Cloud Task Router).
The API endpoint is PUT /api/v2/dataactions/{dataActionId}.
OAuth Scope: dataactions:write
Python Implementation
def update_data_action_timeout(client: PlatformClient, action_id: str, new_timeout_ms: int) -> bool:
"""
Updates the timeout for a specific Data Action.
"""
data_action_api = client.data_actions_api
# 1. Fetch the current action to preserve existing configuration
try:
current_action = data_action_api.get_data_action_by_id(data_action_id)
except ApiException as e:
print(f"Failed to fetch action {data_action_id}: {e}")
return False
# 2. Validate the new timeout
if new_timeout_ms > 10000:
print(f"Error: Timeout cannot exceed 10000ms. Received {new_timeout_ms}ms.")
return False
if new_timeout_ms <= current_action.timeout:
print(f"Skipping {action_id}: New timeout ({new_timeout_ms}) is not greater than current ({current_action.timeout}).")
return False
# 3. Update the timeout field
# The SDK model allows direct attribute modification
current_action.timeout = new_timeout_ms
# 4. Send the update
try:
# The PUT request requires the full entity body
data_action_api.put_data_action(data_action_id, current_action)
print(f"Successfully updated {current_action.name} to {new_timeout_ms}ms.")
return True
except ApiException as e:
if e.status == 409:
# Conflict usually means version mismatch or invalid state
print(f"Conflict updating {data_action_id}: {e.body}")
elif e.status == 422:
# Unprocessable Entity often indicates validation errors (e.g., invalid JSON schema)
print(f"Validation error updating {data_action_id}: {e.body}")
else:
print(f"API Error updating {data_action_id}: {e}")
return False
JavaScript Implementation
async function updateDataActionTimeout(client, actionId, newTimeoutMs) {
const dataActionsApi = client.dataActionsApi;
// 1. Fetch the current action to preserve existing configuration
let currentAction;
try {
const fetchResponse = await dataActionsApi.getDataActionById(actionId);
currentAction = fetchResponse.body;
} catch (error) {
console.error(`Failed to fetch action ${actionId}: ${error.message}`);
return false;
}
// 2. Validate the new timeout
if (newTimeoutMs > 10000) {
console.error(`Error: Timeout cannot exceed 10000ms. Received ${newTimeoutMs}ms.`);
return false;
}
if (newTimeoutMs <= currentAction.timeout) {
console.log(`Skipping ${actionId}: New timeout (${newTimeoutMs}) is not greater than current (${currentAction.timeout}).`);
return false;
}
// 3. Update the timeout field
currentAction.timeout = newTimeoutMs;
// 4. Send the update
try {
// The PUT request requires the full entity body
await dataActionsApi.putDataAction(actionId, currentAction);
console.log(`Successfully updated ${currentAction.name} to ${newTimeoutMs}ms.`);
return true;
} catch (error) {
if (error.status === 409) {
console.error(`Conflict updating ${actionId}: ${error.body}`);
} else if (error.status === 422) {
console.error(`Validation error updating ${actionId}: ${error.body}`);
} else {
console.error(`API Error updating ${actionId}: ${error.message}`);
}
return false;
}
}
Step 3: Processing Results and Handling Pagination
In production environments, you may have hundreds of Data Actions. The GET /api/v2/dataactions endpoint supports pagination. You must iterate through all pages to ensure no slow action is missed.
Python Pagination Logic
def fetch_all_data_actions(client: PlatformClient) -> list:
"""
Fetches all Data Actions handling pagination.
"""
data_action_api = client.data_actions_api
all_actions = []
page_size = 100
page_number = 1
while True:
try:
response = data_action_api.get_data_actions(
page_size=page_size,
page_number=page_number
)
if not response.entities:
break
all_actions.extend(response.entities)
# Check if there are more pages
# The response object contains 'next_page' or we can infer from total
if not hasattr(response, 'next_page') or not response.next_page:
break
page_number += 1
except ApiException as e:
print(f"Error fetching page {page_number}: {e}")
break
return all_actions
Complete Working Example
This Python script combines all steps: authenticates, finds Data Actions with timeouts <= 3000ms, and updates them to 10000ms (the maximum allowed).
import os
import sys
from purecloud_platform_client import Configuration, PlatformClient, PrivateKeyAuthenticator
from purecloud_platform_client.rest import ApiException
def init_client() -> PlatformClient:
config = Configuration()
config.set_property("org_id", os.getenv("GENESYS_ORG_ID"))
config.set_property("client_id", os.getenv("GENESYS_CLIENT_ID"))
config.set_property("private_key_file_path", os.getenv("GENESYS_PRIVATE_KEY_FILE_PATH"))
# Optional: Specify region if not us-east-1
# config.set_property("region", "eu-west-1")
authenticator = PrivateKeyAuthenticator(config)
client = PlatformClient(authenticator)
# Verify Auth
try:
client.get_user_me()
except Exception as e:
raise Exception(f"Authentication failed: {e}")
return client
def main():
# 1. Initialize Client
print("Initializing Genesys Cloud Client...")
try:
client = init_client()
except Exception as e:
print(e)
sys.exit(1)
# 2. Fetch All Data Actions
print("Fetching Data Actions...")
data_action_api = client.data_actions_api
all_actions = []
page_size = 100
page_number = 1
while True:
try:
response = data_action_api.get_data_actions(page_size=page_size, page_number=page_number)
if not response.entities:
break
all_actions.extend(response.entities)
if not hasattr(response, 'next_page') or not response.next_page:
break
page_number += 1
except ApiException as e:
print(f"Error fetching actions: {e}")
break
if not all_actions:
print("No Data Actions found.")
return
print(f"Found {len(all_actions)} Data Actions.")
# 3. Filter and Update
target_timeout = 10000 # 10 seconds (Max allowed)
current_threshold = 3000 # 3 seconds
actions_to_update = [
action for action in all_actions
if action.timeout is not None and action.timeout <= current_threshold
]
print(f"Identified {len(actions_to_update)} actions with timeout <= {current_threshold}ms.")
for action in actions_to_update:
print(f"Processing: {action.name} (ID: {action.id}, Current: {action.timeout}ms)")
# Validate max limit
if target_timeout > 10000:
print("Error: Target timeout exceeds maximum allowed (10000ms).")
break
try:
# Update the timeout
action.timeout = target_timeout
# PUT request
data_action_api.put_data_action(action.id, action)
print(f" -> Updated {action.name} to {target_timeout}ms.")
except ApiException as e:
print(f" -> Failed to update {action.name}: {e.status} {e.reason}")
# Handle specific errors
if e.status == 429:
print(" -> Rate limited. Implement exponential backoff in production.")
elif e.status == 409:
print(" -> Conflict. The action may have been modified by another user.")
print("Update process complete.")
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden
- Cause: The OAuth token does not have the
dataactions:writescope. - Fix: Go to the Genesys Cloud Admin Console > Users > Applications > Edit your Client Application. Add
dataactions:writeto the OAuth Scopes. Re-generate the access token.
Error: 422 Unprocessable Entity
- Cause: The request body contains invalid data. For Data Actions, this often happens if you modify the
type(e.g., trying to change ahttpaction toscript) or if thetimeoutvalue is outside the valid range (1-10000 ms). - Fix: Ensure
timeoutis an integer between 1 and 10000. Do not modify thetypefield during an update.
Error: 409 Conflict
- Cause: Another process modified the Data Action between your
GETandPUTcalls. Genesys Cloud uses optimistic locking. - Fix: Implement a retry loop with exponential backoff. Re-fetch the entity before retrying the
PUT.
Error: Timeout Still Failing After Update
- Cause: The external API you are calling is taking longer than the new timeout (e.g., you set it to 5s but the API takes 6s).
- Fix: Increase the timeout to the maximum 10000ms. If it still fails, your architecture requires asynchronous processing. Use Genesys Cloud Task Router to offload the work or use Webhooks to notify your system asynchronously rather than waiting for a synchronous HTTP response.