How to Implement Customer Lookup via Architect GetExternalContactAction
What You Will Build
- You will configure a Genesys Cloud Architect flow that triggers an external HTTP request to your backend service to retrieve customer data based on the caller’s phone number.
- You will use the
GetExternalContactActionnode to map the inbound caller ID to a specific API endpoint and parse the JSON response into flow variables. - You will implement a Python Flask microservice that accepts the request, queries a mock database, and returns the customer profile in the format expected by Genesys Cloud.
Prerequisites
- Genesys Cloud Account: Access to the Architect tool with permissions to create and publish flows.
- Python 3.9+: Installed with
pipfor the backend service. - Dependencies:
flask,requestsfor the Python backend. - Genesys Cloud SDK (Optional for backend): This tutorial focuses on the raw HTTP interaction for the backend, but you may use the
genesys-cloud-purecloud-platform-clientif you need to update the contact record after lookup. - Public Endpoint: Your backend service must be accessible via HTTPS from the Genesys Cloud data centers. For local testing, use a tunneling service like ngrok.
Authentication Setup
The GetExternalContactAction in Genesys Cloud Architect does not handle OAuth token exchange for your backend service automatically in the standard configuration. You must decide how to authenticate the request coming from Genesys Cloud to your service.
The most common pattern is using a shared secret (API Key) or a simple Bearer token. Below is the setup for a simple Bearer token validation in your Python backend.
# backend_app.py
import os
from flask import Flask, request, jsonify
app = Flask(__name__)
# In production, load this from environment variables
SECRET_TOKEN = "your_secure_secret_token_here"
def authenticate_request():
"""
Validates the Bearer token sent by Genesys Cloud Architect.
"""
auth_header = request.headers.get("Authorization")
if not auth_header:
return False
# Expected format: Bearer <token>
parts = auth_header.split()
if len(parts) != 2 or parts[0] != "Bearer":
return False
return parts[1] == SECRET_TOKEN
@app.route("/api/customer/lookup", methods=["POST"])
def lookup_customer():
if not authenticate_request():
return jsonify({"error": "Unauthorized"}), 401
# Logic continues in Implementation section
pass
In Genesys Cloud Architect, you will configure the GetExternalContactAction to send this token in the Authorization header.
Implementation
Step 1: Configure the Backend Service
Your backend service must accept a POST request containing the caller’s phone number and return a JSON object with customer details. Genesys Cloud expects the response to map to a specific schema.
Create a simple database lookup. For this tutorial, we use a static dictionary.
# backend_app.py (continued)
# Mock database
CUSTOMER_DB = {
"+15551234567": {
"customerId": "CUST-001",
"firstName": "Alice",
"lastName": "Smith",
"tier": "Gold",
"lastInteractionDate": "2023-10-15"
},
"+15559876543": {
"customerId": "CUST-002",
"firstName": "Bob",
"lastName": "Jones",
"tier": "Silver",
"lastInteractionDate": "2023-11-01"
}
}
@app.route("/api/customer/lookup", methods=["POST"])
def lookup_customer():
if not authenticate_request():
return jsonify({"error": "Unauthorized"}), 401
# Parse the incoming JSON body
data = request.get_json()
if not data or "phoneNumber" not in data:
return jsonify({"error": "Missing phoneNumber field"}), 400
phone_number = data["phoneNumber"]
# Normalize phone number if necessary (e.g., remove spaces/dashes)
# For this example, we assume exact match with E.164 format
customer = CUSTOMER_DB.get(phone_number)
if customer:
# Return success with customer data
response_data = {
"status": "success",
"data": customer
}
return jsonify(response_data), 200
else:
# Return not found
response_data = {
"status": "not_found",
"data": None
}
return jsonify(response_data), 200 # Return 200, not 404, to avoid flow error handling complexity unless desired
Run this service locally:
export FLASK_APP=backend_app.py
flask run --port 5000
Use ngrok to expose this locally running server to the internet:
ngrok http 5000
Copy the https://...ngrok.io URL. You will use this in the next step.
Step 2: Configure the GetExternalContactAction in Architect
- Open Genesys Cloud and navigate to Admin > Architect.
- Create a new flow or open an existing IVR/ACD flow.
- Search for the node type Get External Contact Action. Add it to your flow canvas.
- Connect it to your entry point (e.g., after the Greeting node).
Node Configuration
In the node configuration panel, you must define the HTTP request details.
General Settings:
- Name:
Customer Lookup - Description:
Retrieves customer profile based on caller ID
Request Configuration:
- Method:
POST - URL:
https://your-ngrok-url.ngrok.io/api/customer/lookup - Headers: Add a new header.
- Key:
Authorization - Value:
Bearer your_secure_secret_token_here
- Key:
- Content Type:
application/json
Request Body:
You must construct the JSON payload that sends the caller’s phone number to your backend. Use the dynamic variable for the caller’s phone number.
{
"phoneNumber": "{{contact.attributes.caller.phoneNumber}}"
}
Note: The variable {{contact.attributes.caller.phoneNumber}} contains the E.164 formatted phone number of the inbound caller. Ensure your backend expects this exact format.
Response Mapping:
This is the critical step. You must map the JSON response from your backend to Genesys Cloud flow variables.
In the Response Mapping tab, add the following mappings:
| Response Field | Flow Variable | Type |
|---|---|---|
status |
customerLookup.status |
String |
data.customerId |
customerLookup.customerId |
String |
data.firstName |
customerLookup.firstName |
String |
data.lastName |
customerLookup.lastName |
String |
data.tier |
customerLookup.tier |
String |
Error Handling:
- On Error: Connect this port to an error handling node (e.g., a Play Prompt saying “We are experiencing technical difficulties” followed by a Transfer to Agent).
- Timeout: Set to
3000ms (3 seconds). External lookups should be fast.
Step 3: Process Results and Route the Call
After the GetExternalContactAction node, add a Decision node to check if the customer was found.
-
Decision Condition:
- Condition:
customerLookup.statusEqualssuccess - True Path: Connect to a “VIP Customer” path.
- False Path: Connect to a “General Queue” path.
- Condition:
-
VIP Customer Path:
- Add a Play Prompt node.
- Text:
Hello {{customerLookup.firstName}}, welcome back. We see you are a {{customerLookup.tier}} tier member. - Connect to a Select Queue node for a specialized VIP queue.
-
General Queue Path:
- Add a Play Prompt node.
- Text:
Thank you for calling. Please hold for the next available agent. - Connect to a Select Queue node for the general support queue.
Publish the Flow:
Save and publish the flow. Ensure the flow is enabled and assigned to a Routing User or Application that matches the inbound call’s configuration.
Complete Working Example
Backend Service (Python/Flask)
Save this as customer_lookup_service.py.
import os
from flask import Flask, request, jsonify
app = Flask(__name__)
# Configuration
SECRET_TOKEN = os.getenv("LOOKUP_SECRET", "default_secret_change_me")
# Mock Database
CUSTOMER_DB = {
"+15551234567": {
"customerId": "CUST-001",
"firstName": "Alice",
"lastName": "Smith",
"tier": "Gold",
"lastInteractionDate": "2023-10-15"
},
"+15559876543": {
"customerId": "CUST-002",
"firstName": "Bob",
"lastName": "Jones",
"tier": "Silver",
"lastInteractionDate": "2023-11-01"
}
}
def authenticate_request():
"""
Validates the Bearer token sent by Genesys Cloud Architect.
"""
auth_header = request.headers.get("Authorization")
if not auth_header:
return False
parts = auth_header.split()
if len(parts) != 2 or parts[0] != "Bearer":
return False
return parts[1] == SECRET_TOKEN
@app.route("/api/customer/lookup", methods=["POST"])
def lookup_customer():
"""
Endpoint called by Genesys Cloud GetExternalContactAction.
"""
# 1. Authenticate
if not authenticate_request():
return jsonify({"error": "Unauthorized"}), 401
# 2. Parse Input
data = request.get_json()
if not data or "phoneNumber" not in data:
return jsonify({"error": "Missing phoneNumber field"}), 400
phone_number = data["phoneNumber"]
# 3. Lookup Logic
customer = CUSTOMER_DB.get(phone_number)
# 4. Construct Response
if customer:
response_data = {
"status": "success",
"data": customer
}
return jsonify(response_data), 200
else:
response_data = {
"status": "not_found",
"data": None
}
# Return 200 OK even for not found to prevent Architect error port trigger
return jsonify(response_data), 200
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app.run(host="0.0.0.0", port=port)
Architect Flow JSON Snippet
If you are importing flows via JSON, here is the relevant section for the GetExternalContactAction node.
{
"id": "external-contact-node-1",
"type": "GetExternalContactAction",
"name": "Customer Lookup",
"description": "Retrieves customer profile based on caller ID",
"configuration": {
"method": "POST",
"url": "https://your-ngrok-url.ngrok.io/api/customer/lookup",
"headers": {
"Authorization": "Bearer your_secure_secret_token_here",
"Content-Type": "application/json"
},
"body": "{\n \"phoneNumber\": \"{{contact.attributes.caller.phoneNumber}}\"\n}",
"timeout": 3000,
"responseMapping": {
"status": "customerLookup.status",
"data.customerId": "customerLookup.customerId",
"data.firstName": "customerLookup.firstName",
"data.lastName": "customerLookup.lastName",
"data.tier": "customerLookup.tier"
}
},
"edges": {
"success": "decision-node-1",
"error": "error-handler-node-1"
}
}
Common Errors & Debugging
Error: 401 Unauthorized
- Cause: The
Authorizationheader in the Architect node does not match theSECRET_TOKENin your Python backend. - Fix: Verify the token string is identical in both places. Ensure there are no trailing spaces. Check the Python logs for the received header value.
Error: 400 Bad Request
- Cause: The JSON body sent by Genesys Cloud is malformed or missing the
phoneNumberfield. - Fix: Check the variable mapping in the Architect node. Ensure
{{contact.attributes.caller.phoneNumber}}is valid. If the caller did not provide a phone number (e.g., some VoIP providers), this variable may be empty or null. Add a check in your Python code for empty strings.
Error: Timeout (3000ms exceeded)
- Cause: Your backend service is too slow to respond.
- Fix: Optimize your database query. Ensure the network latency between Genesys Cloud data centers and your server is low. Consider increasing the timeout in Architect if necessary, but aim for sub-1-second responses.
Error: Variable Mapping Failure
- Cause: The JSON response from your backend does not match the expected structure in the Architect node.
- Fix: Use a tool like Postman to send a test request to your backend and verify the JSON structure. Ensure the keys in the
responseMappingsection of Architect exactly match the JSON keys returned by your backend. For example, if your backend returns{"data": {"firstName": "Alice"}}, the mapping must bedata.firstName, notfirstName.
Error: Phone Number Format Mismatch
- Cause: Genesys Cloud sends the phone number in E.164 format (e.g.,
+15551234567), but your database stores it differently (e.g.,15551234567). - Fix: Normalize the phone number in your Python backend before lookup. Strip the
+sign or leading zeros if necessary.
# Example normalization in Python
def normalize_phone(phone):
if phone.startswith("+"):
return phone[1:]
return phone