# Sanity Webhooks

## When to Use This Skill

* How do I receive Sanity webhooks?
* How do I verify Sanity webhook signatures?
* Why is my `sanity-webhook-signature` verification failing?
* How do I trigger cache revalidation or search reindexing when a document changes?
* How do I handle document create, update, and delete events from the Content Lake?

## How Sanity Webhooks Work

Sanity uses GROQ-powered webhooks. There are no fixed event-type strings.
Instead, each webhook is configured at [sanity.io/manage](https://www.sanity.io/manage)
with:

* A GROQ filter that decides which document changes fire the webhook (e.g.
  `_type == "post"`, or delta helpers like `delta::changedAny(...)`).
* A GROQ projection that shapes the request body (JSON). If left empty, the
  payload is the whole document after the change, which always includes
  `_id`, `_type`, and `_rev`.

Handlers therefore dispatch on the document's `_type` (and any fields you project),
not on a provider-defined event name. Webhooks fire on create / update / delete
in the Content Lake and ignore draft and version documents by default.

## Verification (core)

Sanity signs with the official [`@sanity/webhook`](https://github.com/sanity-io/webhook-toolkit)
package (v4 requires Node 18+). The `sanity-webhook-signature` header is
Stripe-style — `t=<ms-timestamp>,v1=<sig>` — an HMAC-SHA256 over
``${timestamp}.${rawBody}`` (timestamp in milliseconds), base64url
encoded with no padding. Pass the raw request body — do not `JSON.parse` first.

```javascript
const { isValidSignature, SIGNATURE_HEADER_NAME } = require('@sanity/webhook');
// SIGNATURE_HEADER_NAME === 'sanity-webhook-signature'

const signature = req.headers[SIGNATURE_HEADER_NAME];

// isValidSignature is async in v4+ and returns a boolean (never throws on a
// bad signature). It recomputes the HMAC from the timestamp in the header.
const valid = await isValidSignature(
  rawBody,                              // raw HTTP body string — NOT parsed JSON
  signature,
  process.env.SANITY_WEBHOOK_SECRET,   // secret from sanity.io/manage
);
if (!valid) return res.status(400).send('Invalid signature');

```

No official Python package exists — for FastAPI, verify manually (parse `t`/`v1`,
recompute the base64url HMAC, timing-safe compare). See the FastAPI example.

> For complete handlers with route wiring, event dispatch, and tests, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/sanity-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/sanity-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/sanity-webhooks/examples/fastapi/)

## Document Types (dispatch targets)

There are no fixed events. Dispatch on the projected `_type`. Common studio types:

| `_type` | Triggered when | Common use case |
| --- | --- | --- |
| `post` | A blog post is created/updated/deleted | Revalidate `/blog/[slug]` |
| `author` | An author document changes | Revalidate author pages |
| `product` | A product changes | Revalidate storefront, reindex search |
| `category` | A category changes | Rebuild navigation |
| `page` | A page document changes | Revalidate the page route |

> Docs: [Sanity Webhooks](https://www.sanity.io/docs/webhooks) ·
> [GROQ filters & projections](https://www.sanity.io/docs/webhooks)

## Environment Variables

```bash
SANITY_WEBHOOK_SECRET=your_webhook_secret   # Set when creating the webhook at sanity.io/manage

```

## Delivery & Idempotency

* At-least-once delivery: 1 concurrent request, 2 retries at 30s intervals,
  30s timeout. Don't rely on webhooks as your only source of truth.
* Deduplicate using the `idempotency-key` request header.
* See [webhook-handler-patterns](/webhooks/skills/webhook-handler-patterns)
  for idempotency and retry handling.

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 sanity --path /webhooks/sanity

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/sanity-webhooks/references/overview.md) - GROQ webhook concepts, filters, projections
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/sanity-webhooks/references/setup.md) - Create a webhook at sanity.io/manage, get the secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/sanity-webhooks/references/verification.md) - Signature verification details and gotchas