Webhook not triggering on creation with delta::changedAny filter

4 replies
Last updated: Jul 6, 2023
Webhooks and delta-functions
I'm trying to write a webhook that fires whenever a field in a document gets new information. In my mind, that should happen when a document is created, the field in question is updated, and when the document is deleted.

However, when I set the "filter" field of my webhook to look like this:


_type == "someType" && delta::changedAny(someField)
(and mark my webhook to run on both creation, updating and deleting)

The webhook doesn't trigger on creation. I think it should. Am I doing something wrong, or is this the intended behavior?
AI Update

This is actually intended behavior, and it's a common point of confusion with delta::changedAny(). The function doesn't trigger on document creation because technically, during creation, the field isn't "changing" – it's being set for the first time.

The delta::changedAny() function compares the before and after states of a field. On creation, there is no "before" state to compare against, so the function evaluates to false. This is why your webhook doesn't fire when documents are created.

The Solution

To handle all three scenarios (create, update, delete), you need to adjust your filter to explicitly account for creation. Here are two approaches:

Option 1: Check if the operation is create OR the field changed

_type == "someType" && (delta::operation() == "create" || delta::changedAny(someField))

This uses delta::operation() to explicitly check if it's a creation event, OR if the field changed (which handles updates).

Option 2: Use before() to detect new values

_type == "someType" && (!defined(before().someField) || delta::changedAny(someField))

This checks if the field didn't exist before (creation) OR if it changed (update).

For Deletions

Note that for deletions, your field won't exist in the after() state, so you might want to just filter by type for delete events, or check that the field existed before deletion:

_type == "someType" && (
  delta::operation() == "create" || 
  delta::changedAny(someField) ||
  (delta::operation() == "delete" && defined(before().someField))
)

Consider Sanity Functions Instead

Since you're building automation that reacts to content changes, you might want to consider using Sanity Functions instead of webhooks. Functions are the modern, recommended approach for this type of use case because they:

  • Run natively within Sanity without needing external hosting
  • Have better security and automatic scaling
  • Provide cleaner event handling with built-in document context
  • Can be defined right in your sanity.blueprint.ts file

Webhooks are still great when you need to integrate with external systems that specifically require HTTP callbacks, but for general automation triggered by content changes, Functions are typically the better choice.

You can read more about webhook filters and delta functions in the Sanity documentation.

Show original thread
4 replies
I’ll ask the Content Lake team if that’s the expected behaviour. It may end up that you need to use the following, but I’ll confirm:

_type == "someType" && delta::changedAny(someField) || before().someField == null)
That would work for the creation use case, but not for the deletion usecase.
Hi
user U
. The team got back to me that the
delta::changed*
functions weren’t intended for create/delete cases, and therefore behaviour is not guaranteed. They’ve suggested going with this approach:

_type == "someType" && delta::changedAny(someField) || delta::operation() == "create" || delta::operation() == "delete"
Alright I’ll see if that works in my usecase

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?