EventBridge filter pattern for conversation.end by queue_id

Could someone explain why my EventBridge bus is still receiving conversation.end events for queues outside my target list despite this filter pattern? I am trying to isolate traffic for a specific Slack bot integration in Lagos and the generic match is flooding my webhook endpoint. The filter seems valid JSON but GC is not respecting the queue_id constraint.

{
 "detail-type": ["Conversation End"],
 "detail": {
 "routing.queueId": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
 }
}

Is the routing.queueId field nested differently in the actual event payload than the documentation suggests?

To fix this easily, this is to check the actual json structure sent by genesys cloud. docs state “event details contain nested objects for routing data.” your filter is looking for a flat string array but the api sends an object. i copy-pasted the filter from the aws docs and it failed too. you need to match the exact path.

{
“detail-type”: [“Conversation End”],
“detail”: {
“routing”: {
“queue”: {
“id”: [“a1b2c3d4-e5f6-7890-abcd-ef1234567890”]
}
}
}
}

the suggestion above uses routing.queueId which is not in the schema. i am confused why people assume camelCase works here. it breaks my simple flows. you must use the nested object syntax. also make sure your event bus policy allows the source. docs state “source must match the account id.” if you miss this it returns empty results. i spent two days on this. just use the nested structure.

The way I solve this is by bypassing the EventBridge filter complexity entirely and validating the queue routing logic directly in Python using the Genesys Cloud Analytics API. While the nested JSON path suggested above is technically correct for AWS EventBridge, maintaining those filters is brittle when queue structures change or when you need historical validation. In my Jupyter notebooks, I prefer to query the getInteractionsDelegated endpoint to verify that queue.id matches my target list before trusting the event stream. This approach leverages pandas for robust data handling and avoids the “black box” nature of cloud event filtering.

Here is how I structure the validation check in my notebooks to ensure the queue_id constraint is actually being respected by the platform before it hits any webhook:

import platformclient_v2
from platformclient_v2 import Configuration, AnalyticsApi, PureCloudAuthModule

def validate_queue_events(config: Configuration, queue_id: str):
 """
 Validates if conversation.end events for a specific queue are correctly tagged.
 """
 analytics_api = AnalyticsApi(config)
 
 # Define the query to fetch interactions for the specific queue
 body = platformclient_v2.AnalyticsQueryRequest(
 view="interaction",
 interval="P1D", # Last day
 where=[
 platformclient_v2.AnalyticsQueryPredicate(
 path="routing.queue.id",
 op="eq",
 value=queue_id
 )
 ]
 )
 
 try:
 # Execute the query
 result = analytics_api.post_analytics_interactions_query(body=body)
 
 # Convert to pandas for easier inspection
 import pandas as pd
 df = pd.DataFrame.from_records(result.entities)
 
 if df.empty:
 print(f"No events found for queue {queue_id}. Check EventBridge filter or queue activity.")
 else:
 print(f"Found {len(df)} events. Queue filter is working.")
 print(df[['routing.queue.id', 'routing.queue.name']].head())
 
 except Exception as e:
 print(f"Error querying analytics: {e}")

# Usage
config = PureCloudAuthModule(
 oauth_client_id='your_client_id',
 oauth_client_secret='your_client_secret',
 config=Configuration()
)
validate_queue_events(config, 'a1b2c3d4-e5f6-7890-abcd-ef1234567890')

This method ensures that your data pipeline is only processing relevant records, reducing noise at the source rather than relying solely on AWS-side filtering. It also helps debug why certain events might be missing if the routing.queue.id is null or unexpected in the payload.

This has the hallmarks of a standard case of EventBridge schema mismatch. The suggestion above correctly identifies the nested structure, but there is a critical nuance regarding audit log retention and real-time validation.

I confirmed this pattern works in our Paris region setup. However, relying solely on EventBridge filters for security-critical routing is risky. If the queue is archived or the ID changes, the filter silently fails to match, and you lose visibility. My approach adds a secondary validation layer using the Audit API to ensure the queue still exists and is active.

Here is the robust implementation:

  1. Apply the nested filter in EventBridge:
{
 "detail-type": ["Conversation End"],
 "detail": {
 "routing": {
 "queue": {
 "id": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
 }
 }
 }
}
  1. Validate the queue state in your Lambda handler before processing. Use the PureCloudPlatformClientV2 SDK to check if the queue is active:
from genesyscloud.platform_client_v2 import platform_client

def validate_queue(queue_id):
 api = platform_client.RoutingApi()
 try:
 queue = api.get_routing_queue(queue_id)
 return queue.status == 'enabled'
 except Exception as e:
 log_audit_event("Queue validation failed", queue_id, str(e))
 return False

This ensures that even if the EventBridge filter lets a stale event through, your backend logic rejects it. The audit log API (/api/v2/analytics/events/query) can also be used to backfill data if you need to verify historical conversations for this specific queue ID. Always combine real-time filtering with backend validation for secure automation.

You need to verify the event payload structure before trusting static filters.

  • Inspect actual conversation.end payloads via the EventBridge console to confirm the nested routing.queue.id path.
  • Validate against the getInteractionsDelegated API response schema to ensure field consistency.
  • Use exact queue IDs; partial matches fail silently in strict mode.