Use cases
Webhook reliability patterns (retry, idempotence, replay)
Exponential retry, event_id dedup, manual replay
Scell.io retries each webhook with exponential backoff (5 attempts over 24h: 1min, 5min, 30min, 2h, 12h). On the receiver side you must implement three patterns: (1) verify the HMAC-SHA256 signature in `X-Scell-Signature`, (2) deduplicate on `event_id` to handle legitimate duplicates (network retries, manual replay), (3) respond 2xx within 5s or delivery is considered failed and a retry is scheduled. To replay a webhook use `POST /api/v1/webhooks/{id}/replay` or inspect logs via `GET /api/v1/webhooks/{id}/logs`.
Key facts
- 5 retries over 24h, backoff 1min → 12h
- `X-Scell-Signature` HMAC-SHA256 header
- `X-Scell-Timestamp` anti-replay header (5min tolerance)
- Stable `event_id` UUID, idempotent on receiver side
- `POST /api/v1/webhooks/{id}/replay` endpoint for manual replay
- 30-day persistent logs via `GET /api/v1/webhooks/{id}/logs`
Code example
import { createHmac, timingSafeEqual } from 'node:crypto';
import type { Request, Response } from 'express';
const processed = new Set<string>(); // En prod : Redis SET avec TTL 7j
export async function webhookHandler(req: Request, res: Response) {
const sig = req.header('X-Scell-Signature') ?? '';
const ts = Number(req.header('X-Scell-Timestamp') ?? 0);
const raw = (req as any).rawBody as string;
// 1. Anti-replay
if (Math.abs(Date.now() / 1000 - ts) > 300) return res.sendStatus(401);
// 2. Verify HMAC
const expected = createHmac('sha256', process.env.WEBHOOK_SECRET!).update(`${ts}.${raw}`).digest('hex');
if (!timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return res.sendStatus(401);
// 3. Idempotency
const event = JSON.parse(raw);
if (processed.has(event.event_id)) return res.sendStatus(200);
processed.add(event.event_id);
await handleEvent(event);
res.sendStatus(200); // < 5s
}