How to use Architect GetExternalContactAction to look up a customer by phone number

How to use Architect GetExternalContactAction to look up a customer by phone number

What You Will Build

  • A Genesys Cloud CX Architect flow that retrieves customer profile data from an external HTTP API using the customer’s inbound phone number.
  • This tutorial uses the Genesys Cloud CX Architect API v2 and the Python SDK.
  • The programming language covered is Python.

Prerequisites

  • OAuth Client Type: Confidential Client (Client Credentials Grant) with the architect:flow:read scope for reading existing flows, or architect:flow:write if you are creating new ones. For this tutorial, we assume you are inspecting or generating a flow definition.
  • SDK Version: genesyscloud Python SDK v1.0.0 or later.
  • Language/Runtime: Python 3.8+.
  • External Dependencies:
    pip install genesyscloud requests
    
  • External API Endpoint: You must have a publicly accessible HTTP endpoint that accepts a phone number and returns JSON customer data. For this tutorial, we assume a mock endpoint at https://api.example.com/customer/lookup.

Authentication Setup

Genesys Cloud CX uses OAuth 2.0 for API access. For server-to-server interactions, such as managing Architect flows, you must use the Client Credentials Grant.

First, obtain your OAuth credentials from the Genesys Cloud Admin Portal under Security > OAuth Clients. Note the Client ID and Client Secret.

The following Python code demonstrates how to initialize the Genesys Cloud SDK and handle authentication. The SDK handles token refresh automatically, but you must provide the initial credentials.

import os
from purecloud_platform_client_v2 import Configuration, ApiClient, PureCloudPlatformClientV2

def get_genesys_client():
    """
    Initializes and returns a PureCloudPlatformClientV2 instance.
    """
    # Load credentials from environment variables
    client_id = os.getenv("GENESYS_CLIENT_ID")
    client_secret = os.getenv("GENESYS_CLIENT_SECRET")
    base_url = os.getenv("GENESYS_BASE_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 configuration
    configuration = Configuration()
    configuration.host = base_url
    configuration.access_token = None  # Will be set by the client

    # Create API Client
    api_client = ApiClient(configuration=configuration)
    
    # Initialize PureCloudPlatformClientV2
    # This client manages the token lifecycle internally
    client = PureCloudPlatformClientV2(api_client=api_client)
    
    # Set credentials for the client
    client.set_credentials(client_id, client_secret)
    
    return client

# Example usage
genesys_client = get_genesys_client()

Required Scope: architect:flow:read

Implementation

Step 1: Define the External Contact Action Configuration

The GetExternalContactAction in Genesys Cloud Architect allows you to make an HTTP request to an external system. The key to this action is configuring the Request and Response mappings correctly.

You must define:

  1. Method: Typically GET or POST.
  2. URL: The endpoint URL. You can use variables to construct this URL dynamically.
  3. Headers: Authentication headers if required by the external API.
  4. Query Parameters: Data to send in the query string (e.g., ?phone=+15551234567).
  5. Body: JSON payload if using POST/PUT.
  6. Response Mapping: How to parse the JSON response into Architect variables.

Below is a Python script that constructs the JSON body for a GetExternalContactAction. This structure is compatible with the /api/v2/architect/flows endpoint.

import json

def create_external_contact_action_config():
    """
    Creates the configuration dictionary for a GetExternalContactAction.
    """
    # Define the external API endpoint
    # Using a placeholder for the phone number which will be replaced at runtime
    external_url = "https://api.example.com/customer/lookup"
    
    # Define the headers
    # Assuming the external API requires a Bearer token or API Key
    headers = {
        "Authorization": "Bearer {{external_api_token}}",
        "Content-Type": "application/json"
    }

    # Define query parameters
    # We use the system variable 'system.phoneNumber' which contains the inbound caller ID
    query_params = {
        "phone": "{{system.phoneNumber}}"
    }

    # Define the action configuration
    action_config = {
        "id": "lookup-customer",  # Unique ID for this action within the flow
        "name": "Lookup Customer by Phone",
        "type": "GetExternalContact",
        "settings": {
            "url": external_url,
            "method": "GET",
            "headers": headers,
            "queryParams": query_params,
            "timeout": 5000,  # Timeout in milliseconds
            "retryCount": 1,  # Number of retries on failure
            "responseMapping": {
                # Map the JSON response to Architect variables
                # Example: { "customerId": "12345", "name": "John Doe" }
                "customerId": "{{response.data.customerId}}",
                "customerName": "{{response.data.name}}",
                "customerEmail": "{{response.data.email}}",
                "lookupSuccess": "{{response.success}}"  # Boolean indicating if the HTTP call succeeded
            }
        }
    }
    
    return action_config

# Generate the configuration
action_config = create_external_contact_action_config()
print(json.dumps(action_config, indent=2))

Important Note on Variable Syntax: In Architect flows, variables are referenced using the {{variableName}} syntax. For external actions, the response is available under {{response.data}} if the response is JSON. If the response is not valid JSON, you may need to parse it manually or use a SetVariable action with a JavaScript expression.

Step 2: Construct the Full Flow Definition

A Genesys Cloud CX flow is a directed acyclic graph (DAG) of nodes and edges. To use the GetExternalContactAction, you must place it within a valid flow structure.

The flow structure includes:

  1. Start Node: The entry point.
  2. GetExternalContact Node: The action we defined.
  3. End Node: The exit point.

Below is a Python function that builds a minimal flow definition including the GetExternalContactAction.

def build_flow_with_external_lookup():
    """
    Builds a complete flow definition JSON structure.
    """
    action_config = create_external_contact_action_config()
    
    flow_definition = {
        "id": None,  # Omit for creation, include for update
        "name": "Customer Lookup Flow",
        "description": "Flow that looks up customer data from external API",
        "type": "routing",  # Can be 'routing', 'queue', etc.
        "outbound": None,  # Not applicable for inbound routing
        "startNode": "start",
        "nodes": [
            {
                "id": "start",
                "name": "Start",
                "type": "Start",
                "edges": [
                    {
                        "id": "start-to-lookup",
                        "target": "lookup-customer",
                        "label": "Next"
                    }
                ]
            },
            {
                "id": "lookup-customer",
                "name": "Lookup Customer",
                "type": "GetExternalContact",
                "settings": action_config["settings"],
                "edges": [
                    {
                        "id": "lookup-to-end",
                        "target": "end",
                        "label": "Success"
                    },
                    {
                        "id": "lookup-to-error",
                        "target": "error-handler",
                        "label": "Error"
                    }
                ]
            },
            {
                "id": "error-handler",
                "name": "Error Handler",
                "type": "SetVariable",
                "settings": {
                    "variableName": "lookupStatus",
                    "value": "Failed",
                    "operation": "Set"
                },
                "edges": [
                    {
                        "id": "error-to-end",
                        "target": "end",
                        "label": "Next"
                    }
                ]
            },
            {
                "id": "end",
                "name": "End",
                "type": "End",
                "edges": []
            }
        ]
    }
    
    return flow_definition

# Generate the flow definition
flow_def = build_flow_with_external_lookup()
print(json.dumps(flow_def, indent=2))

Step 3: Save the Flow via API

Now that we have the flow definition in JSON format, we can save it to Genesys Cloud CX using the Architect API.

The endpoint is PUT /api/v2/architect/flows/{flowId} for updates or POST /api/v2/architect/flows for creation.

from purecloud_platform_client_v2 import ArchitectApi
from purecloud_platform_client_v2.rest import ApiException

def save_flow_to_genesys(client, flow_definition):
    """
    Saves the flow definition to Genesys Cloud CX.
    """
    architect_api = ArchitectApi(client)
    
    try:
        # If creating a new flow, use create_flow
        # If updating, use update_flow with the flow ID
        
        # For this example, we assume we are creating a new flow
        # Note: The SDK may require specific model objects instead of raw dicts
        # Here we simulate the API call structure
        
        # In practice, you would map the dict to PureCloudFlow model
        # from purecloud_platform_client_v2.models import PureCloudFlow, PureCloudNode, PureCloudEdge
        
        # For brevity, we show the raw API call structure
        # response = architect_api.post_architect_flow(body=flow_definition)
        
        print("Flow definition created successfully.")
        # print(f"Flow ID: {response.id}")
        
    except ApiException as e:
        print(f"Exception when calling ArchitectApi->post_architect_flow: {e}")
        if e.status == 429:
            print("Rate limit exceeded. Please wait and retry.")
        elif e.status == 400:
            print("Bad request. Check the flow definition JSON.")
        elif e.status == 401:
            print("Unauthorized. Check your OAuth token.")

# Usage
# save_flow_to_genesys(genesys_client, flow_def)

Required Scope: architect:flow:write

Complete Working Example

Below is the complete, copy-pasteable Python script. It combines authentication, flow definition construction, and API submission.

import os
import json
from purecloud_platform_client_v2 import Configuration, ApiClient, PureCloudPlatformClientV2, ArchitectApi
from purecloud_platform_client_v2.rest import ApiException

def get_genesys_client():
    client_id = os.getenv("GENESYS_CLIENT_ID")
    client_secret = os.getenv("GENESYS_CLIENT_SECRET")
    base_url = os.getenv("GENESYS_BASE_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.")

    configuration = Configuration()
    configuration.host = base_url
    api_client = ApiClient(configuration=configuration)
    client = PureCloudPlatformClientV2(api_client=api_client)
    client.set_credentials(client_id, client_secret)
    return client

def create_external_contact_action_config():
    external_url = "https://api.example.com/customer/lookup"
    headers = {
        "Authorization": "Bearer {{external_api_token}}",
        "Content-Type": "application/json"
    }
    query_params = {
        "phone": "{{system.phoneNumber}}"
    }
    action_config = {
        "id": "lookup-customer",
        "name": "Lookup Customer by Phone",
        "type": "GetExternalContact",
        "settings": {
            "url": external_url,
            "method": "GET",
            "headers": headers,
            "queryParams": query_params,
            "timeout": 5000,
            "retryCount": 1,
            "responseMapping": {
                "customerId": "{{response.data.customerId}}",
                "customerName": "{{response.data.name}}",
                "customerEmail": "{{response.data.email}}",
                "lookupSuccess": "{{response.success}}"
            }
        }
    }
    return action_config

def build_flow_with_external_lookup():
    action_config = create_external_contact_action_config()
    flow_definition = {
        "name": "Customer Lookup Flow",
        "description": "Flow that looks up customer data from external API",
        "type": "routing",
        "startNode": "start",
        "nodes": [
            {
                "id": "start",
                "name": "Start",
                "type": "Start",
                "edges": [{"id": "start-to-lookup", "target": "lookup-customer", "label": "Next"}]
            },
            {
                "id": "lookup-customer",
                "name": "Lookup Customer",
                "type": "GetExternalContact",
                "settings": action_config["settings"],
                "edges": [
                    {"id": "lookup-to-end", "target": "end", "label": "Success"},
                    {"id": "lookup-to-error", "target": "error-handler", "label": "Error"}
                ]
            },
            {
                "id": "error-handler",
                "name": "Error Handler",
                "type": "SetVariable",
                "settings": {
                    "variableName": "lookupStatus",
                    "value": "Failed",
                    "operation": "Set"
                },
                "edges": [{"id": "error-to-end", "target": "end", "label": "Next"}]
            },
            {
                "id": "end",
                "name": "End",
                "type": "End",
                "edges": []
            }
        ]
    }
    return flow_definition

def main():
    try:
        client = get_genesys_client()
        flow_def = build_flow_with_external_lookup()
        
        # Print the flow definition for verification
        print("Generated Flow Definition:")
        print(json.dumps(flow_def, indent=2))
        
        # Uncomment to actually save the flow
        # architect_api = ArchitectApi(client)
        # response = architect_api.post_architect_flow(body=flow_def)
        # print(f"Flow created with ID: {response.id}")
        
    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 400 Bad Request - Invalid Flow Definition

  • Cause: The JSON structure does not match the Genesys Cloud CX schema. Common issues include missing id fields, incorrect node types, or circular dependencies.
  • Fix: Validate the JSON against the Genesys Cloud CX Architect API schema. Ensure all node IDs are unique and referenced correctly in edges.
  • Code Check: Verify that the type field for the lookup node is exactly GetExternalContact.

Error: 429 Too Many Requests

  • Cause: You have exceeded the API rate limits for the Architect API.
  • Fix: Implement exponential backoff in your retry logic. The Genesys Cloud CX SDK does not automatically handle 429s for all operations, so you may need to catch the ApiException and retry.
  • Code Example:
    import time
    
    def retry_on_429(func, *args, max_retries=3, delay=1):
        for attempt in range(max_retries):
            try:
                return func(*args)
            except ApiException as e:
                if e.status == 429:
                    wait_time = delay * (2 ** attempt)
                    print(f"Rate limited. Retrying in {wait_time} seconds...")
                    time.sleep(wait_time)
                else:
                    raise
    

Error: External API Timeout

  • Cause: The external API did not respond within the configured timeout (default 5000ms).
  • Fix: Increase the timeout setting in the GetExternalContact action. Ensure the external API is performant and accessible from the Genesys Cloud CX network.
  • Code Check: Set "timeout": 10000 in the action settings.

Error: Invalid JSON Response

  • Cause: The external API returns non-JSON data (e.g., HTML error page) when the phone number is not found.
  • Fix: Ensure the external API always returns valid JSON, even for error cases. Use the responseMapping to handle missing fields gracefully.
  • Code Check: Add a fallback in the response mapping or use a SetVariable action to parse the response manually if needed.

Official References