Handling webhook retry backoff and idempotency when processing AutoSend deliverability events in NestJS

When AutoSend fires deliverability event webhooks (bounces, complaints, deliveries) to our NestJS backend, network retries can deliver duplicate payloads. How do we implement idempotent event processing with Redis?

Why Idempotency Matters for Email Event Webhooks

When handling high-volume email webhooks, network retries or transient 5xx responses can cause AutoSend to retry event notifications. Without idempotency guards, duplicate webhook events can corrupt analytics counters or trigger duplicate database updates.

Implementation with NestJS and Redis:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { RedisService } from './redis.service';

@Injectable()
export class WebhookIdempotencyGuard implements CanActivate {
  constructor(private readonly redis: RedisService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();
    const eventId = req.headers['x-autosend-event-id'] || req.body?.eventId;
    
    if (!eventId) return true; // Fallback if no ID present

    const redisKey = `autosend:evt:${eventId}`;
    // SETNX with a 24-hour TTL ensures duplicate events within 24h are ignored
    const isNew = await this.redis.client.set(redisKey, '1', 'EX', 86400, 'NX');
    
    if (!isNew) {
      // Duplicate event — return 200 OK immediately without processing
      return false; 
    }
    return true;
  }
}

Key Takeaways:

  1. Always respond with HTTP 200 OK on duplicate events so AutoSend stops retrying.
  2. Use Redis SETNX with a TTL to track processed eventId keys efficiently.

Relevant Documentation