Filtering EventBridge conversation.end events for a specific queue in Node.js

Does anyone know how to effectively filter EventBridge events so my Node.js middleware only processes conversation.end events for a specific queue? we’re currently getting flooded with events for every single queue in the org, which is spiking our lambda costs and processing time. i’ve set up a standard SQS subscription via EventBridge, and the raw event payload looks fine, but filtering on the source side seems tricky since the detail object structure varies a bit depending on the event type. here is a snippet of the event detail i’m seeing for a conversation end:

{
 "conversationId": "abc-123",
 "type": "conversation",
 "event": "end",
 "routingData": {
 "queue": {
 "id": "555-queue-id",
 "name": "Support Tier 2"
 }
 }
}

i tried adding a filter policy on the SQS subscription in the Genesys Cloud admin console, but it doesn’t seem to expose the routingData.queue.id field directly in the filter UI options. i’m currently doing the filtering in the lambda handler like this:

const handler = async (event) => {
 const targetQueueId = '555-queue-id';
 for (const record of event.Records) {
 const body = JSON.parse(record.body);
 const detail = JSON.parse(body.Message);
 
 // checking if the queue matches
 if (detail.routingData?.queue?.id === targetQueueId && detail.event === 'end') {
 await processConversationEnd(detail);
 }
 }
};

this works, but it feels inefficient to process and discard thousands of events just to find the ones we care about. is there a way to define this filter in the EventBridge rule itself before it hits SQS? or maybe a specific API call to update the subscription filter that includes the nested queue path? i’ve checked the /api/v2/analytics/events endpoints but that’s for historical data, not real-time subscription management. any pointers on how to tighten this up at the source would be great. i’m running out of ideas on how to reduce the noise without rewriting the whole ingestion pipeline.

The documentation actually says EventBridge rules can filter on nested JSON paths, but you have to get the syntax exactly right or the rule engine just passes everything through. Since conversation.end payloads embed the queue info inside detail.outcomes, you’re not looking at the top level. Here’s a working rule configuration snippet that targets the reason and queueId directly. it uses Equals for the event type and NumericEquals for the ID since Genesys sends IDs as strings in some contexts but numbers in others, so casting helps.

{
 "source": ["genesys.cloud"],
 "detail-type": ["Conversation Ended"],
 "detail": {
 "outcomes": [
 {
 "queueId": {
 "NumericEquals": 123456789
 }
 }
 ]
 }
}

if the queue ID isn’t static, you can’t easily filter on EventBridge itself. you’ll need to push all events to SQS and filter in the Lambda. it’s cheaper than paying for cold starts on filtered events that get dropped immediately. use a simple if check in your handler before processing the business logic. this keeps the compute cost low and avoids the complexity of managing dynamic EventBridge rules via Terraform, which gets messy fast with state drift. just make sure your Lambda timeout is short enough to not block the SQS visibility window.

Make sure you’re not filtering on the event source but on the actual queue ID inside the detail object, otherwise you’ll just get noise.

“detail.outcomes.queueId”: “your-queue-id-here”

i usually handle this in the lambda itself because EventBridge filters can be flaky with nested arrays. just check event.detail.outcomes[0].queueId before doing any heavy lifting.

This looks like a trap. EventBridge filtering on nested arrays inside detail.outcomes is unreliable because the array order isn’t guaranteed, so you’ll still process noise.

  • validate the queueId in your lambda handler before doing any work
  • ignore complex EventBridge patterns for this specific payload structure