Migrate legacy webhook behavior to GROQ-powered webhooks
To recreate the payload shape of legacy webhooks, follow these steps. This does not restore dataset-level batching — GROQ-powered webhooks fire once per changed document, so each request carries exactly one ID. Batch on your receiving endpoint if you need dataset-level grouping.
- In the project's management interface at manage.sanity.io, create a webhook set to trigger on create, update, and delete.
- Leave the Filter field empty.
- Add the following to the Projection field:
{
"projectId": sanity::projectId(),
"dataset": sanity::dataset(),
"ids": {
"created": select(before() == null && after() != null => [_id], []),
"deleted": select(before() != null && after() == null => [_id], []),
"updated": select(before() != null && after() != null => [_id], []),
"all": [
_id
]
}
}The transaction ID is not available to the projection, but every webhook request carries it in the sanity-transaction-id header. By default this webhook does not fire for drafts. or versions. documents — enable the drafts and versions settings if you need those, and note that version support requires webhook API version v2025-02-19 or later.
You can also create a webhook from a template that has these settings.