# 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.