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