Skip to main content

Patterns

Webhook replay mechanism

/replay endpoint and event_id-based deduplication

A webhook may fail on the receiver side (5xx, timeout, ongoing deploy). The replay pattern exposes a `POST /api/v1/webhooks/{id}/replay` endpoint that retransmits a specific event (by `event_id`) or a time range. On the receiver side, deduplication is mandatory: store each received `event_id` in a `processed_webhook_events` table with a unique index, or use Redis with `SETNX event:{id} 1 EX 86400`. Without deduplication, a replay double-invoices, double-accounts, double-triggers a workflow. Full pattern: automatic retry with exponential backoff (5 attempts over 24h) → if all fail, event is marked `failed` and user can trigger manual replay via dashboard or API. Delivery logs are kept 30 days via `GET /api/v1/webhooks/{id}/logs`.

Key facts

  • `POST /api/v1/webhooks/{id}/replay` endpoint with event_id or range
  • Mandatory deduplication on receiver (SQL table or Redis SETNX)
  • Automatic 5x retry over 24h with exponential backoff before 'failed'
  • Delivery logs kept 30d via `GET /webhooks/{id}/logs`
  • Manual replay available in Scell.io dashboard

Code example

// Receveur webhook avec déduplication Redis
import { createClient } from 'redis';
const redis = createClient();

async function handleWebhook(req: Request, res: Response) {
  const eventId = req.body.event_id;
  const isFirstTime = await redis.set(
    `webhook:event:${eventId}`,
    '1',
    { NX: true, EX: 86400 } // 24h dedup window
  );
  if (!isFirstTime) return res.status(200).send('duplicate ignored');

  // Traiter l'événement (idempotent par construction)
  await processEvent(req.body);
  return res.status(200).send('ok');
}

See also

Your cookie preferences

We use cookies to improve your experience. Essential cookies are always active. Cookie policy.