# Fastify webhook handler
## Buffer parser on the webhook route
```ts
import Fastify from 'fastify'
import { verifyWebhook } from '@clerk/backend/webhooks'
const fastify = Fastify({ logger: true })
fastify.addContentTypeParser(
'application/json',
{ parseAs: 'buffer' },
async (req, body, done) => {
done(null, body)
},
)
fastify.post('/api/webhooks', async (request, reply) => {
try {
const evt = await verifyWebhook(request)
// handle evt by evt.type, write to DB
return reply.code(200).send({ received: true })
} catch (err) {
request.log.error(err)
return reply.code(400).send({ error: 'verification failed' })
}
})
```
`parseAs: 'buffer'` hands the handler the untouched bytes. The default parser would hand you parsed JSON and verification would fail on every event.
## Checklist
- Scope the buffer parser to the webhook route's context if the rest of the app wants parsed JSON. A global buffer parser forces every route to parse manually.
- Do not register `clerkPlugin()` on the webhook route's context, and do not call `getAuth()` there. Webhooks have no session; session auth on this route is a bug.
- `CLERK_WEBHOOK_SIGNING_SECRET` per endpoint; dev and prod differ.
- Return 200 after side effects; Clerk retries on 4xx/5xx. Idempotency: key your DB write on the event id or `evt.data.id` so redelivery is a no-op.
- If only some events fail verification, suspect a proxy rewriting bodies for larger payloads, not the parser.