← ClaudeAtlas

webhook-handler-patternlisted

Build a webhook handler with signature verification, idempotency, and dead-letter routing. Per-source patterns (Stripe, Resend, GitHub). Self-contained — no harness wrappers required.
voidcorp-core/void-harness · ★ 0 · Code & Development · score 76
Install: claude install-skill voidcorp-core/void-harness
# webhook-handler-pattern Use when adding any inbound webhook endpoint (Stripe, Resend, GitHub, custom). Webhooks are **untrusted POST endpoints** that fire from external systems — every wrong handler is either a security breach (forged events accepted), a duplicate-charge bug (no idempotency), or a silent failure (no dead-letter). ## Location ``` apps/<app>/src/app/api/webhooks/<source>/route.ts ``` One folder per source. Path stable (external systems POST to a fixed URL — never rename without coordinating). ## The 5 non-negotiable layers ``` 1. Signature verification — verify the event came from the source 2. Idempotency — same event delivered twice = one effect 3. Zod validation — parsed shape matches what handler expects 4. Service call — the business work (in apps/<app>/src/services/) 5. Acknowledgment — return 2xx on success; specific codes on failure ``` Skipping ANY of these is a Sev-2 waiting. The pattern below shows the 5 layers explicit — no wrapper assumed. ## Canonical handler (explicit, self-contained) ```ts // apps/web/src/app/api/webhooks/stripe/route.ts import { NextRequest, NextResponse } from 'next/server'; import Stripe from 'stripe'; import { z } from 'zod'; import { db } from '@/adapters/db'; import { env, logger } from '@repo/core'; import * as Sentry from '@sentry/nextjs'; import { handleStripeEvent } from '@/services/billing/stripe'; const stripe = new Stripe(env.STRIPE_SECRET_KEY); // 3. Zod schema