GROQ Webhooks: changedAny() syntax and triggering on both create and update

9 replies
Last updated: Oct 8, 2021
Hi, I have two questions about GROQ webhooks:

changedAny syntaxLooks like the
docs here are off when you do
delta::changedAny(<more than one field>)
more. I had to do two paretheses, e.g
delta::changedAny((name, phone))
, not to get invalid GROQ.

Trigger on both update and created with changedAny So, what’s the best practice (if it exists any best practices yet :)) when registrering webhooks. I’m trying to make a webhook for a specific type of documents fire on both creation
and on update only when specific fields has changed. I’m trying
_type == "customer" && delta::changedAny((field1, field2, ...))
. This hook won’t fire on creation because if the
delta::changedAny
I guess.
So do you recommend registering two separate webhooks in this case? Or is there a way to say
“fire on both creation, and on update iff these fields changed”?
AI Update

Great questions! You're absolutely right about both issues.

changedAny Syntax with Double Parentheses

The documentation has been updated to reflect this, but you do indeed need double parentheses when using delta::changedAny() with multiple fields:

delta::changedAny((name, phone))

The inner parentheses create an array of field names, which is what the function expects.

Triggering on Both Creation and Field Changes

For your second question about firing webhooks on both document creation and updates when specific fields change, the recommended approach is to use a single webhook with a condition that checks for document creation:

_type == "customer" && (delta::changedAny((field1, field2)) || before() == null)

The before() == null check returns true when a document is being created (since there's no "before" state). You could also use before()._createdAt == null but before() == null is cleaner and works just as well.

This approach is better than two separate webhooks because:

  • It's DRY (Don't Repeat Yourself)
  • It reduces redundant HTTP requests and bandwidth usage
  • It's easier to maintain

You can also use this pattern in your webhook projection to determine if an event was a creation:

{
  "created": before() == null,
  // ... other fields
}

Note that while webhooks are great for many use cases, consider Sanity Functions for more complex event-driven logic, as they provide serverless compute directly within Sanity with native integration and better security.

Show original thread
9 replies
Hi Stian. Thanks for pointing out about the required extra parentheses. I’ll take a look.
For your second question, I expect one webhook would be better because it’s dry—unless it’s not possible. I’m just testing something now.
Yep, my thought as well:• One webhook is DRY
• And it’s easier to avoid redundant roundtrips and bytes over the wire #climatechange
🙂
Yep, my though as well:• One webhooks i DRY
• And it’s fewer bytes over the wire #climatechange
🙂
I don’t know if this is the best approach, but I figured you could use
before()._createdAt == null
to test for the creation use case.

_type == 'customer' && (delta::changedAny((name, phone)) || before()._createdAt == null)
Ah, of couse. Could also do
before() == null
, perhaps?
That was my initial plan in the projection:

{
  "created": before() == null,
}
Using just
before()
does seem to work as well.
Thanks for quick help! I think we’re trying to go for one webhook per document type then 🙂
You’re welcome! Just updating those guides to reflect your observation. Thanks again for pointing that out.

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?