# Pages Router webhook handler

## Disable the body parser

```ts
export const config = {
  api: { bodyParser: false },
}
```

Without this, Next parses the JSON body before your handler runs and the Svix signature will never match.

## Buffer the raw body, then verify

```ts
import { verifyWebhook } from '@clerk/backend/webhooks'

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).end()
  }
  try {
    const evt = await verifyWebhook(req)
    // handle evt by evt.type, write to DB
    return res.status(200).json({ received: true })
  } catch (err) {
    console.error('webhook verification failed', err)
    return res.status(400).json({ error: 'verification failed' })
  }
}
```

`verifyWebhook` from `@clerk/backend/webhooks` accepts the Node request; with `bodyParser: false` the raw stream is intact for it to read.

## Checklist

- The route must be public: no `getAuth(req)` gate, no middleware protection on `/api/webhooks`. Webhooks carry no session.
- `CLERK_WEBHOOK_SIGNING_SECRET` must be the secret for this exact endpoint. Rotating or recreating the endpoint invalidates the old secret.
- Return 200 after your side effects. Non-2xx triggers retries with backoff; design the handler idempotent (key on the event id or `evt.data.id`) because retries redeliver.
- If verification fails only in production, compare secrets first (dev vs prod endpoint), then check that no proxy or CDN is altering the body or stripping the `svix-*` headers.