Webhook Processing at Scale: Idempotency, Signature Verification, and Async Queues
Webhooks are the backbone of event-driven SaaS integrations. Stripe fires one when a payment succeeds. GitHub fires one when a PR is merged. Vercel fires one when a deploy completes. Here's how to ...

Source: DEV Community
Webhooks are the backbone of event-driven SaaS integrations. Stripe fires one when a payment succeeds. GitHub fires one when a PR is merged. Vercel fires one when a deploy completes. Here's how to receive, verify, and process them reliably. The Core Problem: Idempotency Webhooks are delivered at-least-once. Your endpoint will receive duplicates. Every webhook handler must be idempotent — processing the same event twice must produce the same result as processing it once. // Bad -- creates duplicate records on retry export async function POST(req: Request) { const event = await req.json() await db.order.create({ data: parseOrder(event) }) return Response.json({ ok: true }) } // Good -- idempotent via upsert on event ID export async function POST(req: Request) { const event = await req.json() await db.webhookEvent.upsert({ where: { eventId: event.id }, create: { eventId: event.id, processed: false, payload: event }, update: {}, // no-op if already exists }) // Process asynchronously await