EventBridge deduplication strategy for Genesys Cloud routing events

Is there a clean way to implement idempotency when consuming Genesys Cloud events via EventBridge?

I am seeing duplicate routing:queueConversation events hitting my consumer Lambda. The duplicates are not identical; they have different eventId values but represent the same logical state change (e.g., a conversation moving from waiting to accepted within a 500ms window). Genesys Cloud’s documentation mentions eventual consistency, but it does not provide a clear field for deduplication in the EventBridge payload.

Currently, my consumer writes to DynamoDB using the conversationId as the primary key. I am using a conditional write expression to prevent overwrites:

dynamodb.update_item(
 Key={'conversationId': event['conversationId']},
 UpdateExpression='SET #status = :status, #updated_at = :updated_at',
 ExpressionAttributeNames={'#status': 'status', '#updated_at': 'updated_at'},
 ExpressionAttributeValues={':status': event['status'], ':updated_at': event['timestamp']},
 ConditionExpression='attribute_not_exists(#updated_at) OR #updated_at < :updated_at'
)

This works for most cases, but when the duplicate event arrives with the exact same timestamp (down to the millisecond, which happens occasionally with high-volume routing changes), the condition fails or the update is skipped incorrectly. I am trying to avoid complex windowing logic in my Lambda.

ConditionalCheckFailedException: The conditional request failed

Is there a recommended pattern for handling this? Should I be relying on the eventId from the Genesys Cloud webhook header (which is lost in the EventBridge translation layer) or is there a unique identifier in the EventBridge record I should be using instead? I have checked the detail object, but nothing stands out as a unique transaction ID for the event itself.

The problem here is relying on eventId for deduplication. Use conversationId and timestamp instead.

  1. Extract routing:conversationId from the event payload.
  2. Store a hash of conversationId + stateChange in DynamoDB with a short TTL.
  3. Check existence before processing. Ignore if present.

This handles logical duplicates regardless of eventId variance.

Check your partition key strategy. Using conversationId alone is fine for low volume, but high-volume queues will cause hot partitions in DynamoDB. Hash the conversationId with a random salt or use a composite key including the agent ID to distribute writes.

This seems like a classic eventual consistency trap. The suggestion above about DynamoDB hashing is solid for scale. I verify this in my Newman runs by checking for duplicate conversationId in the event logs. See the deduplication guide here: https://support.genesys.com/s/article/EventBridge-Deduplication.

This is a standard eventual consistency issue.

  • Use Redis for deduplication instead of DynamoDB to avoid hot partitions.
  • Store a hash of conversationId + state with a 500ms TTL.
  • Check existence before processing.