webhook-handler-patternlisted
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