How to Implement a Phone Number Lookup Using Genesys Cloud Architect GetExternalContactAction
What You Will Build
- This tutorial demonstrates how to configure the Genesys Cloud Architect flow to pause execution and look up customer data from an external API using the
GetExternalContactAction. - This implementation uses the Genesys Cloud Platform API (v2) for flow configuration and the PureCloud Platform Client SDK for validation and testing.
- The code examples are provided in Python using the
purecloud-platform-client-v2SDK and raw HTTP requests for the external mock service.
Prerequisites
- OAuth Client Type: A Genesys Cloud OAuth Client with the
flow:allscope to create and modify Architect flows. - SDK Version:
purecloud-platform-client-v2>= 190.0.0 (Python). - Language/Runtime: Python 3.9+.
- External Dependencies:
requests,flask(for the local mock server),purecloud-platform-client-v2. - External API Endpoint: You must have a publicly accessible HTTP endpoint that accepts a JSON payload containing a phone number and returns customer details. For this tutorial, we will build a local Flask server that can be exposed via ngrok or similar tunneling tools.
Authentication Setup
Genesys Cloud uses OAuth 2.0 for API authentication. For script-based development, the Client Credentials flow is standard.
import os
from purecloud_platform_client_v2 import Configuration, ApiClient, PlatformApi
def get_purecloud_api_client() -> ApiClient:
"""
Initializes and returns a configured PureCloud API client.
"""
# Load 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 must be set in environment.")
configuration = Configuration(
client_id=client_id,
client_secret=client_secret,
host=env_url,
oauth_host=env_url.replace("api.", "login.")
)
api_client = ApiClient(configuration)
return api_client
Required Scopes:
flow:all: Required to create, update, and publish Architect flows.flow:view: Required to read existing flows.
Implementation
Step 1: Build the External Lookup Service
Before configuring Genesys, you must have a target endpoint. The GetExternalContactAction sends an HTTP POST request to your defined URL. The payload typically contains the contact details collected in the flow (e.g., phone number, email).
Create a simple Flask server to simulate your CRM or database lookup.
# mock_lookup_service.py
from flask import Flask, request, jsonify
import os
app = Flask(__name__)
# Mock database
CUSTOMER_DB = {
"+12025550147": {
"id": "CUST-001",
"name": "Alice Smith",
"tier": "Gold",
"last_call": "2023-10-01"
},
"+12025550199": {
"id": "CUST-002",
"name": "Bob Jones",
"tier": "Silver",
"last_call": "2023-09-15"
}
}
@app.route('/api/v1/customer-lookup', methods=['POST'])
def lookup_customer():
"""
Accepts a phone number and returns customer data.
Genesys sends the payload in the body.
"""
try:
data = request.get_json()
if not data:
return jsonify({"error": "No JSON payload provided"}), 400
phone_number = data.get('phoneNumber')
if not phone_number:
return jsonify({"error": "phoneNumber field missing"}), 400
# Simulate lookup
customer = CUSTOMER_DB.get(phone_number)
if customer:
# Return 200 OK with the customer data
return jsonify(customer), 200
else:
# Return 404 if not found. Genesys will handle this in the flow.
return jsonify({"error": "Customer not found"}), 404
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
# Run on port 5000
app.run(port=5000, debug=True)
Note: Genesys Cloud servers cannot reach localhost. You must expose this endpoint using a tool like ngrok, localtunnel, or deploy it to a cloud function (AWS Lambda, Azure Function) before proceeding.
Step 2: Construct the Architect Flow JSON
Architect flows are defined as JSON objects. To use GetExternalContactAction, you must define a flow with a specific structure. The critical components are:
- Start Node: Entry point.
- GetExternalContact Node: The action that calls your API.
- End Node: Termination.
The GetExternalContactAction requires:
url: The external endpoint.method: HTTP method (usuallyPOST).body: A template string mapping flow variables to the request body.timeout: Time in milliseconds to wait for a response.
Below is the Python code to construct this JSON payload using the SDK models. This ensures type safety and correct field naming.
from purecloud_platform_client_v2.models import (
Flow,
FlowType,
FlowNode,
StartNode,
EndNode,
GetExternalContactNode,
GetExternalContactAction,
HttpMethod,
FlowEdge,
FlowNodeSet
)
from datetime import datetime
def build_lookup_flow(external_url: str) -> dict:
"""
Constructs the JSON payload for a flow that looks up a customer by phone number.
"""
# 1. Define the Start Node
start_node = FlowNode(
id="start-node",
type="StartNode",
label="Start",
metadata={
"position": {"x": 100, "y": 100}
},
configuration=StartNode()
)
# 2. Define the GetExternalContact Node
# This is the core action. We map the 'phoneNumber' variable from the flow
# into the JSON body sent to the external API.
external_action = GetExternalContactAction(
url=external_url,
method=HttpMethod.POST,
body='{ "phoneNumber": "{{phoneNumber}}" }',
timeout=5000, # 5 seconds timeout
headers={
"Content-Type": "application/json"
}
)
lookup_node = FlowNode(
id="lookup-node",
type="GetExternalContactNode",
label="Lookup Customer",
metadata={
"position": {"x": 100, "y": 250}
},
configuration=GetExternalContactNode(
action=external_action
)
)
# 3. Define the End Node
end_node = FlowNode(
id="end-node",
type="EndNode",
label="End",
metadata={
"position": {"x": 100, "y": 400}
},
configuration=EndNode()
)
# 4. Define Edges
# Connect Start -> Lookup
edge_start_to_lookup = FlowEdge(
id="edge-1",
from_node_id="start-node",
to_node_id="lookup-node",
label="To Lookup"
)
# Connect Lookup -> End
# In a real flow, you might split based on the response status (200 vs 404).
# For this basic example, we connect directly to End.
edge_lookup_to_end = FlowEdge(
id="edge-2",
from_node_id="lookup-node",
to_node_id="end-node",
label="To End"
)
# 5. Assemble the Flow Object
flow_nodes = FlowNodeSet(
nodes=[start_node, lookup_node, end_node],
edges=[edge_start_to_lookup, edge_lookup_to_end]
)
flow = Flow(
type=FlowType.INBOUND,
name="Phone Lookup Flow",
description="Looks up customer by phone number using external API",
nodes=flow_nodes,
metadata={
"version": 1,
"lastModified": datetime.utcnow().isoformat()
}
)
return flow
Step 3: Create and Publish the Flow via API
Once the flow object is constructed, you must send it to the Genesys Cloud API. The flow must be published to become active.
from purecloud_platform_client_v2 import FlowApi
import json
def create_and_publish_flow(api_client: ApiClient, external_url: str) -> str:
"""
Creates a new flow and publishes it. Returns the Flow ID.
"""
flow_api = FlowApi(api_client)
# Build the flow JSON
flow_obj = build_lookup_flow(external_url)
try:
# Step 1: Create the Flow
# Note: The API expects the flow object. The SDK handles serialization.
create_response = flow_api.post_flow(body=flow_obj)
flow_id = create_response.id
print(f"Flow created successfully with ID: {flow_id}")
# Step 2: Publish the Flow
# A flow is not active until published.
# We need to update the flow status to 'published'.
# First, get the current version to ensure we are updating the latest draft.
get_response = flow_api.get_flow(flow_id=flow_id)
# Update the metadata to indicate publication
# In real scenarios, you often increment the version manually or let Genesys handle it.
# Here we simply call the publish endpoint.
publish_response = flow_api.post_flow_publish(flow_id=flow_id)
print(f"Flow published successfully. Version: {publish_response.version}")
return flow_id
except Exception as e:
print(f"Error creating or publishing flow: {e}")
raise
# Usage
if __name__ == "__main__":
client = get_purecloud_api_client()
# Replace with your ngrok/public URL
MY_PUBLIC_URL = "https://abc123.ngrok.io/api/v1/customer-lookup"
flow_id = create_and_publish_flow(client, MY_PUBLIC_URL)
Step 4: Handling the Response in Architect
When the GetExternalContactAction completes, Genesys Cloud stores the response in a system variable named externalContact. This variable contains:
externalContact.responseCode: HTTP status code (e.g., 200, 404).externalContact.responseBody: The raw JSON string returned by your API.externalContact.responseHeaders: Headers from the response.
To use the data, you typically use a Set Variables node after the Lookup node to parse the JSON.
- Add a Set Variables node after
lookup-node. - Map the parsed data to flow variables. Since
responseBodyis a string, you may need to use thejson.parse()function in the expression if your flow logic requires object access, or simply pass the string to downstream nodes.
Example JSON mapping in a Set Variables node:
{
"customerName": "{{externalContact.responseBody.name}}",
"customerTier": "{{externalContact.responseBody.tier}}",
"lookupSuccess": "{{externalContact.responseCode == 200}}"
}
Error Handling:
If your external API returns a 4xx or 5xx error, the GetExternalContactAction will still complete, but the responseCode will reflect the error. You should add a Split node or Condition node after the lookup to branch logic based on externalContact.responseCode.
Complete Working Example
Below is the complete Python script that sets up the API client, builds the flow, and publishes it. Ensure you have your environment variables set.
import os
import sys
from datetime import datetime
from purecloud_platform_client_v2 import (
Configuration,
ApiClient,
FlowApi,
Flow,
FlowType,
FlowNode,
StartNode,
EndNode,
GetExternalContactNode,
GetExternalContactAction,
HttpMethod,
FlowEdge,
FlowNodeSet
)
def get_purecloud_api_client() -> ApiClient:
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 must be set.")
configuration = Configuration(
client_id=client_id,
client_secret=client_secret,
host=env_url,
oauth_host=env_url.replace("api.", "login.")
)
return ApiClient(configuration)
def build_lookup_flow(external_url: str) -> Flow:
start_node = FlowNode(
id="start",
type="StartNode",
label="Start",
metadata={"position": {"x": 100, "y": 100}},
configuration=StartNode()
)
# The core action
action = GetExternalContactAction(
url=external_url,
method=HttpMethod.POST,
body='{ "phoneNumber": "{{phoneNumber}}" }',
timeout=5000,
headers={"Content-Type": "application/json"}
)
lookup_node = FlowNode(
id="lookup",
type="GetExternalContactNode",
label="Lookup Customer",
metadata={"position": {"x": 100, "y": 250}},
configuration=GetExternalContactNode(action=action)
)
end_node = FlowNode(
id="end",
type="EndNode",
label="End",
metadata={"position": {"x": 100, "y": 400}},
configuration=EndNode()
)
edges = [
FlowEdge(id="e1", from_node_id="start", to_node_id="lookup", label="To Lookup"),
FlowEdge(id="e2", from_node_id="lookup", to_node_id="end", label="To End")
]
nodes = FlowNodeSet(nodes=[start_node, lookup_node, end_node], edges=edges)
return Flow(
type=FlowType.INBOUND,
name="Dev: Phone Lookup",
description="External contact lookup by phone",
nodes=nodes,
metadata={"version": 1, "lastModified": datetime.utcnow().isoformat()}
)
def main():
try:
client = get_purecloud_api_client()
flow_api = FlowApi(client)
# Use your public endpoint here
EXTERNAL_URL = "https://YOUR_NGROK_URL.ngrok.io/api/v1/customer-lookup"
flow = build_lookup_flow(EXTERNAL_URL)
print("Creating flow...")
created_flow = flow_api.post_flow(body=flow)
flow_id = created_flow.id
print(f"Flow created: {flow_id}")
print("Publishing flow...")
publish_result = flow_api.post_flow_publish(flow_id=flow_id)
print(f"Flow published. Version: {publish_result.version}")
print("Success. You can now test this flow in Architect or trigger it via an IVR.")
except Exception as e:
print(f"Failed: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 401 Unauthorized
Cause: The OAuth token is expired or invalid.
Fix: Ensure your GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET are correct. The ApiClient handles token refresh automatically, but if you are manually managing tokens, ensure you are using the latest access token.
Error: 400 Bad Request (Flow Validation)
Cause: The flow JSON is malformed. Common issues include missing id fields on nodes, circular references, or invalid HTTP methods.
Fix:
- Verify that all node IDs are unique within the flow.
- Ensure the
GetExternalContactActionurlis a valid HTTPS URL. HTTP is not allowed for security reasons. - Check that the
bodytemplate uses valid Mustache syntax (e.g.,{{variableName}}).
Error: 403 Forbidden
Cause: The OAuth client lacks the flow:all scope.
Fix: Go to the Genesys Cloud Admin Console → Security → OAuth Clients. Edit your client and add the flow:all scope. Re-authenticate to get a new token with the updated scopes.
Error: External Contact Timeout
Cause: Your external API took longer than the timeout value (default 5000ms) to respond.
Fix: Increase the timeout in the GetExternalContactAction configuration. Ensure your external service is optimized for low latency. If the API is slow, consider async patterns or caching.
Error: JSON Parse Error in Flow
Cause: The external API returned non-JSON data or an HTML error page (e.g., 502 Bad Gateway from a load balancer).
Fix:
- Test the external endpoint independently using
curlor Postman to ensure it returns valid JSON with a 200 status code. - In the flow, add a Set Variables node to capture
externalContact.responseBodyand log it to the transcript or a database for debugging.