Cas d’usage
Patterns de fiabilité des webhooks (retry, idempotence, replay)
Retry exponentiel, déduplication par event_id, replay manuel
Scell.io retry chaque webhook en backoff exponentiel (5 tentatives sur 24h : 1min, 5min, 30min, 2h, 12h). Côté receveur, vous devez implémenter trois patterns : (1) vérifier la signature HMAC-SHA256 dans `X-Scell-Signature`, (2) dédupliquer sur `event_id` pour gérer les doublons légitimes (network retry, replay manuel), (3) répondre 2xx en moins de 5s sinon la livraison est considérée comme échouée et un retry est planifié. Pour rejouer un webhook, utilisez `POST /api/v1/webhooks/{id}/replay` ou consultez les logs via `GET /api/v1/webhooks/{id}/logs`.
À retenir
- 5 tentatives sur 24h, backoff 1min → 12h
- Header `X-Scell-Signature` HMAC-SHA256
- Header `X-Scell-Timestamp` anti-replay (tolérance 5min)
- `event_id` UUID stable, idempotent côté receveur
- Endpoint `POST /api/v1/webhooks/{id}/replay` pour rejeu manuel
- Logs persistants 30 jours via `GET /api/v1/webhooks/{id}/logs`
Exemple de code
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
}