How to filter EventBridge events to only receive conversation.end for a specific queue
What You Will Build
- You will configure an AWS EventBridge rule that filters Genesys Cloud Webhook events to trigger only when a conversation ends in a specific queue.
- You will use the Genesys Cloud
webhooksAPI to register the endpoint and the AWS SDK to define the event pattern. - The tutorial covers Python for the Genesys Cloud configuration and TypeScript for the AWS Lambda handler that processes the filtered event.
Prerequisites
- Genesys Cloud: An organization with API access, a defined Queue ID, and permissions to manage Webhooks (
webhooks:manage). - AWS: An AWS account with permissions to create EventBridge rules and Lambda functions (
events:PutRule,lambda:CreateFunction). - SDKs:
- Python:
genesys-cloud-sdk(latest version). - TypeScript:
@aws-sdk/client-eventbridgeand@aws-sdk/client-lambda.
- Python:
- Runtime: Python 3.9+ or Node.js 18+.
Authentication Setup
Genesys Cloud uses OAuth 2.0 Client Credentials flow for server-to-server communication. You must obtain an access token before calling the Webhooks API.
Python: Obtaining an OAuth Token
import os
import requests
from typing import Optional
def get_genesys_token(client_id: str, client_env: str) -> Optional[str]:
"""
Retrieves an OAuth access token from Genesys Cloud.
"""
token_url = f"https://{client_env}.mypurecloud.com/oauth/token"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
data = {
"grant_type": "client_credentials"
}
# Use HTTP Basic Auth for the client credentials
auth = (client_id, os.getenv("GENESYS_CLIENT_SECRET"))
try:
response = requests.post(token_url, headers=headers, data=data, auth=auth)
response.raise_for_status()
return response.json().get("access_token")
except requests.exceptions.RequestException as e:
print(f"Failed to obtain token: {e}")
return None
Required Scope: webhooks:manage
Implementation
Step 1: Configure the Genesys Cloud Webhook
Before filtering in AWS, you must ensure Genesys Cloud sends the events to your AWS EventBridge endpoint. Genesys Cloud supports direct integration with AWS EventBridge.
You will create a webhook of type eventbridge. The critical part is defining the targets and ensuring the eventTypes include conversation.end.
Python: Create EventBridge Webhook
import os
from genesyscloud.rest import Configuration
from genesyscloud.webhooks.api import WebhooksApi
from genesyscloud.webhooks.model import WebhookEventBridgeTarget, Webhook, WebhookEventBridgeConfig
from genesyscloud.webhooks.model_webhook_event_bridge_target import WebhookEventBridgeTarget
from genesyscloud.webhooks.model_webhook_event_type import WebhookEventType
def create_eventbridge_webhook(token: str, client_env: str, region: str, event_bus_arn: str) -> str:
"""
Creates a Genesys Cloud Webhook targeting AWS EventBridge.
"""
configuration = Configuration()
configuration.host = f"https://{client_env}.mypurecloud.com"
configuration.access_token = token
# Initialize the API client
webhooks_api = WebhooksApi(configuration)
# Define the target
target = WebhookEventBridgeTarget(
event_bus_arn=event_bus_arn,
region=region
)
# Define the event types we want to send
# Note: We send ALL conversation.end events here.
# Filtering by queue happens in EventBridge Rule.
event_types = [
WebhookEventType(name="conversation.end")
]
# Create the webhook payload
webhook = Webhook(
name="Genesys-to-EventBridge-Filter",
description="Sends conversation.end events to AWS EventBridge",
enabled=True,
targets=[target],
event_types=event_types
)
try:
# Post the webhook
response = webhooks_api.post_webhooks(body=webhook)
print(f"Webhook created with ID: {response.id}")
return response.id
except Exception as e:
print(f"Error creating webhook: {e}")
raise
Required Scope: webhooks:manage
Why this approach? Genesys Cloud does not support filtering by Queue ID at the webhook registration level for EventBridge targets. It sends all conversation.end events to the Event Bus. The filtering burden shifts to AWS EventBridge, which is more performant and cost-effective because EventBridge rules are processed before invoking your Lambda function.
Step 2: Define the AWS EventBridge Rule with Filter
Now you will create an EventBridge rule that listens to the event bus and filters for conversation.end events where the queue.id matches your specific queue.
TypeScript: Create EventBridge Rule
import { EventBridgeClient, PutRuleCommand, PutTargetsCommand } from "@aws-sdk/client-eventbridge";
import { LambdaClient, CreateFunctionCommand } from "@aws-sdk/client-lambda";
import { readFileSync } from "fs";
import { gzipSync } from "zlib";
const REGION = process.env.AWS_REGION || "us-east-1";
const QUEUE_ID = process.env.GENESYS_QUEUE_ID || "your-queue-id-here"; // Replace with actual Queue ID
const EVENT_BUS_NAME = process.env.EVENT_BUS_NAME || "default";
const eventbridge = new EventBridgeClient({ region: REGION });
async function createEventBridgeRule(lambdaArn: string) {
// 1. Define the Event Pattern
// This pattern filters for:
// - Source: com.genesys.cloud.webhooks
// - Detail Type: conversation.end
// - Detail.queue.id: The specific queue ID
const eventPattern = {
source: ["com.genesys.cloud.webhooks"],
detailType: ["conversation.end"],
detail: {
queue: {
id: [QUEUE_ID]
}
}
};
// 2. Create the Rule
const ruleName = `GenesysQueueFilter-${QUEUE_ID}`;
const putRuleCommand = new PutRuleCommand({
Name: ruleName,
EventBusName: EVENT_BUS_NAME,
EventPattern: JSON.stringify(eventPattern),
State: "ENABLED",
Description: "Filters Genesys conversation.end events for specific queue"
});
try {
const ruleResponse = await eventbridge.send(putRuleCommand);
console.log(`Rule ARN: ${ruleResponse.Arn}`);
// 3. Attach Lambda Target
const putTargetsCommand = new PutTargetsCommand({
EventBusName: EVENT_BUS_NAME,
Rule: ruleName,
Targets: [
{
Id: "1",
Arn: lambdaArn,
// Optional: InputTransformer to clean up the payload if needed
// InputPathsMap: { "queueId": "$.detail.queue.id" },
// InputTemplate: '{"queueId": <queueId>}'
}
]
});
await eventbridge.send(putTargetsCommand);
console.log(`Lambda target attached to rule ${ruleName}`);
return ruleResponse.Arn;
} catch (error) {
console.error("Error creating EventBridge rule:", error);
throw error;
}
}
Key Filter Logic:
The detail.queue.id field in the Genesys Cloud event payload contains the UUID of the queue. By specifying [QUEUE_ID] in the EventPattern, EventBridge drops any event where the queue ID does not match. This ensures your Lambda function is never invoked for other queues, saving compute costs.
Step 3: Process the Filtered Event in Lambda
Your Lambda function will only receive events that passed the filter. However, you must still handle the payload structure correctly.
TypeScript: Lambda Handler
import { Context, Handler } from "aws-lambda";
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
const dynamoDb = new DynamoDBClient({ region: REGION });
export const handler: Handler = async (event: any, context: Context) => {
console.log("Received filtered event:", JSON.stringify(event, null, 2));
// 1. Validate the event structure
if (!event.detail || !event.detail.conversation) {
throw new Error("Invalid event structure: missing detail.conversation");
}
const conversationId = event.detail.conversation.id;
const queueId = event.detail.queue.id;
const wrapUpCode = event.detail.wrapUpCode;
const duration = event.detail.conversation.duration;
// 2. Process the data (e.g., save to DynamoDB)
const params = {
TableName: "GenesysConversations",
Item: {
conversationId: { S: conversationId },
queueId: { S: queueId },
wrapUpCode: { S: wrapUpCode || "NONE" },
durationMs: { N: duration.toString() },
timestamp: { S: new Date().toISOString() }
}
};
try {
await dynamoDb.send(new PutItemCommand(params));
return {
statusCode: 200,
body: JSON.stringify({ message: "Event processed successfully" })
};
} catch (error) {
console.error("Error processing event:", error);
throw error;
}
};
Expected Event Payload Structure:
The event.detail object contains the Genesys Cloud event payload. For conversation.end, it includes:
conversation.id: The unique identifier of the conversation.queue.id: The ID of the queue the conversation was associated with.wrapUpCode: The wrap-up code selected by the agent.conversation.duration: The total duration of the conversation in milliseconds.
Step 4: Deploy the Lambda Function
You need to deploy the TypeScript code as a Lambda function.
TypeScript: Deploy Lambda
async function deployLambda() {
const lambda = new LambdaClient({ region: REGION });
// Zip the handler code
const code = readFileSync("./handler.ts");
const zipBuffer = gzipSync(code);
const createFunctionCommand = new CreateFunctionCommand({
FunctionName: "GenesysQueueProcessor",
Runtime: "nodejs18.x",
Role: process.env.LAMBDA_ROLE_ARN || "arn:aws:iam::role/lambda-role",
Handler: "handler.handler",
Code: { ZipFile: zipBuffer },
Timeout: 10,
MemorySize: 128
});
try {
const response = await lambda.send(createFunctionCommand);
console.log(`Lambda Function ARN: ${response.FunctionArn}`);
return response.FunctionArn;
} catch (error) {
console.error("Error creating Lambda function:", error);
throw error;
}
}
Complete Working Example
This is a complete Node.js script that orchestrates the creation of the Lambda function and the EventBridge rule.
import { createEventBridgeRule } from "./eventbridge";
import { deployLambda } from "./lambda";
async function main() {
try {
// 1. Deploy the Lambda function
const lambdaArn = await deployLambda();
console.log("Lambda deployed successfully.");
// 2. Create the EventBridge rule and attach the Lambda
const ruleArn = await createEventBridgeRule(lambdaArn);
console.log("EventBridge rule created successfully.");
// 3. Verify the setup
console.log("Setup complete. Genesys Cloud webhooks will now trigger this pipeline only for the specified queue.");
} catch (error) {
console.error("Setup failed:", error);
process.exit(1);
}
}
main();
Common Errors & Debugging
Error: Event not reaching Lambda
Cause: The EventBridge rule filter is too strict or incorrect.
Fix: Verify the queue.id in the EventPattern matches the exact Queue ID from Genesys Cloud. Use the AWS Console to view “Failed Invocations” or “Matched Events” in the EventBridge rule details.
Debugging Code:
// In the Lambda handler, add a debug log for all events to verify filtering
console.log("Event Detail Type:", event.detailType);
console.log("Event Queue ID:", event.detail?.queue?.id);
Error: 403 Forbidden from Genesys Cloud
Cause: The Webhook target in Genesys Cloud is not allowed to send events to the specified Event Bus ARN.
Fix: Ensure the Event Bus ARN is correct and that the AWS account has the necessary permissions to receive events from external sources. Also, verify that the Genesys Cloud Webhook is enabled.
Error: Lambda Timeout
Cause: The Lambda function takes too long to process the event.
Fix: Increase the Lambda timeout or optimize the processing logic. For simple logging or database writes, 10 seconds is usually sufficient.