Webhooks, cache purges, and the five minutes after publish
Publishing is only half a write. The other half is everything that cached the old version, and a webhook is how all of it finds out at once.
Publishing is only half a write. The other half is everything that cached the old version — the CDN, the static build, the search index, the partner's nightly import — and a webhook is how all of it finds out at once instead of within the hour.
Fire on the event, filter in the payload
A webhook that fires on every write is one that rebuilds a site because somebody fixed a typo in a draft. The event says what happened; the filter says whether this listener cares, and `_type == "post"` is most of the difference between a useful webhook and a queue nobody reads.
The projection matters for the same reason. Sending the whole document means the receiver has the whole document's shape to keep up with; sending the four fields it actually uses means the next field you add is not somebody else's deploy.
A webhook is a promise to a system you do not control. Send it less than you could.
What a delivery record should tell you
- Which webhook, which document, which event.
- Which attempt this was, and how long it took.
- What the receiver answered, status code included.
- Whether it succeeded, without reading a log to find out.
Retries with backoff are table stakes. What actually saves an afternoon is being able to answer "did the deploy hear about this post" from the studio rather than from a log console — which is a question about the delivery record, not about the delivery.
*[_type == "post" && publishedAt > $since] | order(publishedAt asc) { _id, title, slug }
That is also the query a receiver runs to catch up after a missed delivery, which is worth designing for: a webhook that can be replayed by asking for everything since a timestamp is a webhook you can trust on a bad network.
Related posts
Your content model is the product decision
Every content system fails the same way: a field that grew a second meaning, and a template that reads it both ways. The model is the one thing you will still be living with in three years.
GROQ in twenty minutes
There is no join, because there is no second table. A query says which documents and then what to return, and everything that looks like syntax is one of those two halves made more specific.
Draft, publish, and the two-row trick
Achar stores a draft as a second document whose id begins `drafts.` rather than a flag on one row. It looks like duplication, and it is the reason a headline can be rewritten for a week without touching what the site serves.
