๐ง Idempotency in webhook processing
Webhooks can be delivered more than once (retries, network issues). An idempotency key ensures that processing the same event multiple times has the same effect โ no duplicate charges, no double notifications.
- Storage strategies: Redis with TTL (e.g., 24h) for fast lookups, or a database dedup table with a unique constraint on the key column. Always expire old keys.
- Key properties: Time-sortable keys (UUIDv7, ULID) improve database index performance. High entropy reduces collision risk.
๐ Code example: idempotent handler
// Python (Flask)
@app.post('/webhook')
def webhook():
key = request.headers.get('Idempotency-Key')
if redis.exists(key):
return {'status': 'already processed'}, 200
# process event ...
redis.setex(key, 86400, 'processed')
return {'status': 'ok'}, 201
// Node.js (Express)
app.post('/webhook', async (req, res) => {
const key = req.headers['idempotency-key'];
const exists = await db.query('SELECT 1 FROM dedup WHERE key = $1', [key]);
if (exists.rows.length) return res.status(200).json({ status: 'duplicate' });
// process ...
await db.query('INSERT INTO dedup (key, created) VALUES ($1, NOW())', [key]);
res.status(201).json({ status: 'created' });
});