Genesys Cloud EventBridge: Duplicate Events and Deduplication Strategy

We are observing duplicate events in our EventBridge consumer for userPresenceChange. The event-id is unique, but the payload content is identical within a 5-second window. Our current deduplication logic in the Lambda handler is failing due to race conditions. How do you handle idempotency? Is there a client-side way to suppress duplicates via the webhook subscription API, or must we rely on a distributed cache?

{
 "source": "genesys.cloud",
 "event-id": "a1b2c3d4",
 "detail": {
 "type": "userPresenceChange",
 "userId": "12345"
 }
}

If I remember correctly, you can’t suppress duplicates at the source, but you can use idempotency keys in Redis.
await redis.set(f"dedup:{event_id}", "1", nx=True, ex=10)
See https://support.genesys.cloud/articles/eventbridge-deduplication-strategies

The problem here is relying on basic Redis SET operations without considering the latency of your GraphQL gateway’s request pipeline. If you are batching these events via DataLoader, a simple nx check might fail if the batch is processed before the cache write completes across shards. You need a deterministic deduplication strategy at the resolver level before hitting the cache.

  1. Extract the event-id from the EventBridge payload.
  2. Use a local in-memory Set for immediate sub-millisecond deduplication within the same Node.js process.
  3. Fallback to Redis only if the local Set is full or for cross-instance scaling.

Here is the resolver logic I use in my gateway:

const localCache = new Set();
const MAX_LOCAL_CACHE = 1000;

async function handleEvent(event) {
 const eventId = event['event-id'];
 
 // 1. Fast path: Local memory check
 if (localCache.has(eventId)) {
 console.log(`[Dedup] Local hit: ${eventId}`);
 return null;
 }

 // 2. Add to local cache with LRU eviction
 if (localCache.size >= MAX_LOCAL_CACHE) {
 localCache.clear(); // Simple flush for high-throughput
 }
 localCache.add(eventId);

 // 3. Slow path: Distributed cache check (Redis)
 const redisKey = `dedup:${eventId}`;
 const exists = await redis.exists(redisKey);
 
 if (exists) {
 console.log(`[Dedup] Redis hit: ${eventId}`);
 return null;
 }

 // 4. Set in Redis with short TTL (5s window mentioned by OP)
 await redis.set(redisKey, '1', 'EX', 5);
 
 // Process event...
 return processEvent(event);
}

This approach prevents race conditions because the local check handles 99% of duplicates instantly. The Redis call is only made for new events, reducing I/O pressure. Do not use nx alone; combine it with the local Set for idempotency at scale.

resource "genesyscloud_eventbridge_subscription" "presence" {
 name = "Presence Dedup"
 event_type = "userPresenceChange"
 dedup_window = "PT5S"
}

The easiest way to fix this is to leverage the native dedup_window parameter in the Terraform module. This enforces server-side idempotency, eliminating the need for external Redis caches or complex Lambda logic.

You need to verify the EventBridge subscription configuration. The dedup_window attribute in Terraform is not a native Genesys Cloud property. It is a provider-specific abstraction that may not map correctly to the underlying API. I suspect your duplicates stem from mismatched event filters or missing scope permissions rather than a missing dedup window. Check these items:

  • Actual API response for GET /api/v2/events/subscriptions
  • OAuth scope eventbridge:subscribe
  • Event payload structure for userPresenceChange