Filter AWS EventBridge Events for Genesys Cloud Conversation End by Queue
What You Will Build
- You will configure an AWS EventBridge Rule that filters Genesys Cloud
conversation.endevents to trigger a Lambda function only for a specific queue. - You will use the Genesys Cloud EventBridge integration API to verify the target ARN and the AWS EventBridge Console API to define the input pattern.
- The tutorial covers Python (Boto3) for infrastructure verification and JSON for EventBridge input pattern definition.
Prerequisites
- Genesys Cloud: An active organization with EventBridge integration enabled. You need an OAuth client with
analytics:events:readandintegration:events:readscopes. - AWS Account: Permissions to create EventBridge Rules and Lambda functions. You need the ARN of your target Lambda function.
- Python 3.9+: With
boto3andrequestsinstalled. - Queue ID: The internal Genesys Cloud ID of the queue you want to filter (e.g.,
a1b2c3d4-e5f6-7890-abcd-ef1234567890).
Authentication Setup
You need two distinct authentication contexts: Genesys Cloud OAuth for API calls and AWS IAM credentials for Boto3.
Genesys Cloud OAuth
Use a standard Client Credentials flow. Store the access token in an environment variable for reuse.
import requests
import json
import os
def get_genesys_token(client_id: str, client_secret: str) -> str:
"""
Retrieves a Genesys Cloud OAuth access token.
"""
url = "https://api.mypurecloud.com/oauth/token"
data = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret
}
response = requests.post(url, data=data)
if response.status_code != 200:
raise Exception(f"Failed to get token: {response.status_code} - {response.text}")
token_data = response.json()
return token_data["access_token"]
# Usage
GENESYS_TOKEN = get_genesys_token(
os.getenv("GENESYS_CLIENT_ID"),
os.getenv("GENESYS_CLIENT_SECRET")
)
AWS Credentials
Configure your AWS credentials using standard AWS CLI configuration or environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION). Boto3 will automatically pick these up.
Implementation
Step 1: Identify the EventBridge Source and Target ARN
Before filtering, you must confirm the exact Source ARN or Event Source identifier Genesys Cloud uses. This ensures you do not accidentally filter out other critical events or create a rule that never matches.
We query the Genesys Cloud EventBridge integration to find the target ARN associated with your organization.
import requests
def get_eventbridge_targets(access_token: str) -> list:
"""
Retrieves the list of EventBridge targets configured in Genesys Cloud.
"""
url = "https://api.mypurecloud.com/api/v2/integrations/eventbridge/targets"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise Exception(f"Failed to fetch targets: {response.status_code} - {response.text}")
return response.json()["entities"]
targets = get_eventbridge_targets(GENESYS_TOKEN)
for target in targets:
print(f"Target ARN: {target['arn']}")
print(f"Target Status: {target['state']}")
Required Scope: integration:events:read
Expected Response:
[
{
"id": "abc123",
"arn": "arn:aws:lambda:us-east-1:123456789012:function:MyGenesysProcessor",
"state": "ACTIVE",
"eventTypes": ["conversation.start", "conversation.end", "interaction.start"]
}
]
Step 2: Define the EventBridge Input Pattern
This is the core logic. EventBridge filters events based on a JSON path match. Genesys Cloud sends events to EventBridge with a specific structure.
The conversation.end event payload from Genesys Cloud looks like this in EventBridge:
{
"source": "com.genesyscloud.platform",
"detail-type": "conversation.end",
"detail": {
"conversationId": "uuid-here",
"queueId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"routingType": "queue",
"mediaType": "voice",
"direction": "inbound"
}
}
To filter for a specific queue, you must filter on detail.queueId.
Create a file named eventbridge_filter.json:
{
"source": ["com.genesyscloud.platform"],
"detail-type": ["conversation.end"],
"detail": {
"queueId": ["YOUR_SPECIFIC_QUEUE_ID_HERE"]
}
}
Critical Notes:
- Array Values: EventBridge input patterns require arrays for values, even if you only have one match. Use
["queue-id"], not"queue-id". - Field Existence: If the conversation was not routed through a queue (e.g., a direct transfer),
queueIdmay be missing. The filter will correctly exclude these events. - Case Sensitivity: Field names are case-sensitive.
queueIdis correct;queueidwill fail.
Step 3: Create the EventBridge Rule via Boto3
Now, apply this filter using AWS Boto3. This step creates the rule that connects Genesys Cloud events to your Lambda, but only for the specified queue.
import boto3
import json
import os
def create_filtered_eventbridge_rule(queue_id: str, lambda_arn: str, rule_name: str) -> dict:
"""
Creates an EventBridge rule that filters Genesys conversation.end events for a specific queue.
Args:
queue_id: The Genesys Cloud Queue ID to filter on.
lambda_arn: The ARN of the target Lambda function.
rule_name: The name for the new EventBridge rule.
Returns:
The response from the put_rule API call.
"""
events_client = boto3.client('events')
# Define the input pattern
input_pattern = {
"source": ["com.genesyscloud.platform"],
"detail-type": ["conversation.end"],
"detail": {
"queueId": [queue_id]
}
}
try:
# 1. Create the Rule
response = events_client.put_rule(
Name=rule_name,
EventPattern=json.dumps(input_pattern),
State='ENABLED',
Description=f'Filter Genesys conversation.end for queue {queue_id}'
)
print(f"Rule created: {response['RuleArn']}")
# 2. Add the Target (Lambda)
# Note: You need to ensure the Lambda has an EventBridge permission policy
# This is often handled by the 'aws_lambda_permission' resource in CloudFormation
# or via 'add_permission' API call. Here we just attach the target.
events_client.put_targets(
Rule=rule_name,
Targets=[
{
'Id': '1',
'Arn': lambda_arn
}
]
)
print(f"Target added: {lambda_arn}")
return response
except events_client.exceptions.ResourceNotFoundException:
raise Exception("EventBridge service not available in this region.")
except Exception as e:
raise Exception(f"Failed to create rule: {str(e)}")
# Usage
QUEUE_ID = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" # Replace with your Queue ID
LAMBDA_ARN = "arn:aws:lambda:us-east-1:123456789012:function:MyGenesysProcessor" # Replace with your Lambda ARN
RULE_NAME = "GenesysConversationEndSpecificQueue"
# Ensure Lambda has permission to be invoked by EventBridge
lambda_client = boto3.client('lambda')
try:
lambda_client.add_permission(
FunctionName=LAMBDA_ARN.split(':')[-1], # Extract function name from ARN
StatementId='AllowEventBridgeInvoke',
Action='lambda:InvokeFunction',
Principal='events.amazonaws.com',
SourceArn=f"arn:aws:events:{os.getenv('AWS_DEFAULT_REGION')}:123456789012:rule/{RULE_NAME}"
)
print("Lambda permission added.")
except lambda_client.exceptions.ResourceConflictException:
print("Lambda permission already exists.")
create_filtered_eventbridge_rule(QUEUE_ID, LAMBDA_ARN, RULE_NAME)
Required AWS Permissions:
events:PutRuleevents:PutTargetslambda:AddPermission
Step 4: Verify the Filter in AWS Console
To ensure the filter is applied correctly, you can list the rules and inspect the event pattern.
def describe_eventbridge_rule(rule_name: str) -> dict:
"""
Retrieves details of an existing EventBridge rule.
"""
events_client = boto3.client('events')
try:
response = events_client.describe_rule(Name=rule_name)
print(f"Rule Name: {response['Name']}")
print(f"State: {response['State']}")
print(f"Event Pattern: {response['EventPattern']}")
return response
except events_client.exceptions.ResourceNotFoundException:
print(f"Rule {rule_name} not found.")
return None
describe_eventbridge_rule(RULE_NAME)
Complete Working Example
This script combines authentication, target verification, and rule creation into a single executable module.
import requests
import boto3
import json
import os
import sys
def get_genesys_token(client_id: str, client_secret: str) -> str:
url = "https://api.mypurecloud.com/oauth/token"
data = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret
}
response = requests.post(url, data=data)
if response.status_code != 200:
raise Exception(f"Token error: {response.text}")
return response.json()["access_token"]
def verify_genesys_target(access_token: str) -> str:
"""Returns the first active EventBridge target ARN."""
url = "https://api.mypurecloud.com/api/v2/integrations/eventbridge/targets"
headers = {"Authorization": f"Bearer {access_token}"}
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise Exception(f"Target fetch error: {response.text}")
targets = response.json().get("entities", [])
for t in targets:
if t.get("state") == "ACTIVE":
return t["arn"]
raise Exception("No active EventBridge targets found in Genesys Cloud.")
def setup_eventbridge_filter(queue_id: str, lambda_arn: str, rule_name: str):
events_client = boto3.client('events')
lambda_client = boto3.client('lambda')
region = os.getenv('AWS_DEFAULT_REGION', 'us-east-1')
# 1. Define Filter
input_pattern = {
"source": ["com.genesyscloud.platform"],
"detail-type": ["conversation.end"],
"detail": {
"queueId": [queue_id]
}
}
# 2. Create Rule
try:
events_client.put_rule(
Name=rule_name,
EventPattern=json.dumps(input_pattern),
State='ENABLED',
Description=f'Genesys conversation.end for queue {queue_id}'
)
print(f"[Success] Rule '{rule_name}' created.")
except Exception as e:
print(f"[Error] Failed to create rule: {e}")
return
# 3. Add Target
try:
events_client.put_targets(
Rule=rule_name,
Targets=[{'Id': '1', 'Arn': lambda_arn}]
)
print(f"[Success] Target added to rule.")
except Exception as e:
print(f"[Error] Failed to add target: {e}")
return
# 4. Add Lambda Permission
func_name = lambda_arn.split(':')[-1]
rule_arn = f"arn:aws:events:{region}:{os.getenv('AWS_ACCOUNT_ID')}:rule/{rule_name}"
try:
lambda_client.add_permission(
FunctionName=func_name,
StatementId='AllowEventBridgeInvokeSpecificQueue',
Action='lambda:InvokeFunction',
Principal='events.amazonaws.com',
SourceArn=rule_arn
)
print(f"[Success] Lambda permission added.")
except lambda_client.exceptions.ResourceConflictException:
print("[Info] Lambda permission already exists.")
except Exception as e:
print(f"[Error] Failed to add Lambda permission: {e}")
if __name__ == "__main__":
# Configuration
GENESYS_CLIENT_ID = os.getenv("GENESYS_CLIENT_ID")
GENESYS_CLIENT_SECRET = os.getenv("GENESYS_CLIENT_SECRET")
TARGET_QUEUE_ID = os.getenv("TARGET_QUEUE_ID") # e.g., a1b2c3d4-...
TARGET_LAMBDA_ARN = os.getenv("TARGET_LAMBDA_ARN")
RULE_NAME = os.getenv("RULE_NAME", "GenesysQueueFilter")
if not all([GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, TARGET_QUEUE_ID, TARGET_LAMBDA_ARN]):
print("Missing environment variables.")
sys.exit(1)
try:
token = get_genesys_token(GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET)
target_arn = verify_genesys_target(token)
print(f"Verified Genesys Target: {target_arn}")
setup_eventbridge_filter(TARGET_QUEUE_ID, TARGET_LAMBDA_ARN, RULE_NAME)
except Exception as e:
print(f"Fatal Error: {e}")
sys.exit(1)
Common Errors & Debugging
Error: Rule Created but Lambda Not Invoked
Cause: The EventBridge rule is created, but the Lambda function lacks the permission to be invoked by EventBridge, or the queueId in the Genesys event does not match the filter exactly.
Fix:
- Check CloudWatch Logs for the Lambda function. Look for
User arn:aws:events:... is not authorized to invoke function. - If this error appears, run the
add_permissionstep again. Ensure theSourceArnmatches the exact ARN of the EventBridge rule. - Verify the
queueIdin the filter. Genesys Cloud Queue IDs are UUIDs. If you copied the ID from the URL, ensure it is the full UUID, not a truncated version.
Error: ResourceNotFoundException for EventBridge
Cause: The AWS region specified in your AWS credentials does not support EventBridge or the rule name already exists in a different state.
Fix:
- Ensure
AWS_DEFAULT_REGIONis set to a region where EventBridge is available (most major regions). - Check if the rule already exists using
describe_eventbridge_rule. If it does, update it instead of creating a new one.
Error: queueId Not Present in Event
Cause: The conversation ended without being associated with a queue (e.g., a direct transfer, a chat transfer, or an internal note).
Fix:
- If you need to capture all conversation ends regardless of queue, remove the
queueIdfilter from the input pattern. - If you strictly need queue-based filtering, this is expected behavior. The event is correctly filtered out.
Error: 401 Unauthorized on Genesys API
Cause: The OAuth token is expired or the client credentials are incorrect.
Fix:
- Verify
GENESYS_CLIENT_IDandGENESYS_CLIENT_SECRETin your environment variables. - Ensure the OAuth client has the
integration:events:readscope.