Sign-ups are closed. Menestrel now powers the sites built by Agora Studio, a web agency based in France.

Publishing should end at "live", not at a webhook

Every headless CMS stops at its own API and fires a webhook. What happens in the gap between that webhook and a page a visitor can actually see, and why nobody owns it.

Ask a headless CMS what happens when an editor clicks Publish and you get a precise answer: the entry’s status changes, the content becomes available through the delivery API, and a webhook fires if one is configured.

Ask what happens after that and the answer becomes vague, because after that is not their problem.

For a static site, “after that” is the entire remaining distance between a click and a page a human can see. This article is about that gap, why it exists, and what it costs.

The gap, step by step

Here is the actual chain for a static site with a headless CMS:

  1. Editor clicks Publish.
  2. CMS writes the change and marks it published.
  3. CMS sends an HTTP request to a build hook.
  4. The host queues a build.
  5. The build starts, installs dependencies, fetches content, generates pages.
  6. The build succeeds or fails.
  7. On success, the host promotes the deployment.
  8. The CDN starts serving the new version.
  9. Caches expire and the change is visible everywhere.

The CMS owns steps 1 to 3. The host owns 4 to 8. Nobody owns the fact that the editor is waiting, and nobody owns telling them what happened.

The editor’s experience is: click, see a confirmation, and then nothing. The confirmation means “step 2 happened”, which they reasonably read as “my change is on the website”. Those are different claims, separated by six steps that can each fail.

The failure modes nobody sees

The build fails. A dependency published a broken version, a required field was empty in a way the template did not handle, the host had a bad ten minutes. The build goes red. The editor was told “published”. The old page keeps being served. Everyone believes the site is updated.

This is the one that costs money. A restaurant publishes new prices on Thursday, the build fails, and they serve customers at the old prices all weekend because their site says so.

The hook was never called. Webhook URLs get regenerated, secrets rotate, someone deletes an integration during a cleanup. Now Publish writes to a database and nothing else ever happens. There is no error, because from the CMS’s point of view everything worked.

The build succeeded but was queued behind another. Two editors publish within a minute. Depending on the host, you get two builds, or one cancelled build, or two builds racing where the older one wins. The second editor’s change disappears without any error anywhere.

The build ran before the content was readable. The hook fires the instant the database write commits. If content is served through a cache or a read replica, the build can start and fetch the previous state. The build is green, the deployment is live, and the change is not in it. This one is genuinely hard to debug because everything reports success.

Why webhooks stop there

Not an oversight: it is a boundary that makes sense for the vendor.

A general-purpose headless CMS serves mobile apps, kiosks and server-rendered sites as well as static ones. Most of those have no build to track. Committing to “we will tell you when your page is live” means understanding your host, holding a token for it, polling its API, and handling every failure mode of a system they do not control.

So they stop at the boundary of their own system, which is defensible engineering and leaves a real hole for anyone building static sites.

What owning the gap requires

Closing it means a handful of unglamorous things.

Freeze what you publish. Publishing should produce an immutable snapshot with a stable identifier, not a pointer to mutable state. Otherwise a build that starts at 14:31 and finishes at 14:33 may contain a change made at 14:32, or may not, depending on caching. With a frozen snapshot, a given build corresponds to exactly one content state, and reverting means pointing at an earlier one.

Put the snapshot somewhere that is not you. If the build reads from your API, your outage is your customers’ outage, at the worst possible moment. Putting the snapshot on a CDN means a build succeeds even while the CMS is down. We verify this by switching our API off and confirming a client’s deployment still completes.

Hold credentials for the host. Triggering a build takes a hook URL. Knowing whether it succeeded takes an API token. That is a secret per site, which must be encrypted at rest, never displayed after entry, and revocable.

Poll, and give up. Ask the host for the deployment status until it is terminal or a timeout passes. A build stuck for forty-five minutes is a failure, whatever the host says.

Group and rate-limit. Two publishes in a minute should produce one build. Without that, an editor fixing three typos triggers three builds, of which two are wasted and the third might race.

Break the circuit. If a target fails repeatedly, stop calling it and tell someone. Retrying a broken deploy hook forever is how you find out about a problem from your client instead of from your monitoring.

Report honestly. In progress, live at a specific time, or failed with a reason. Never a green tick for “we wrote a row”.

None of this is clever. It is a fan-out with tracking, retries and a circuit breaker, which is well-understood engineering. It is simply work that sits between two products and therefore usually gets done by nobody.

What the editor should see

The interface consequence is small and worth stating, because it is the whole point.

The editor clicks Publish. The button shows the change is in progress. It becomes “Live at 2:32 pm”. If it fails, it says so, with something they can act on: retry, or tell the developer, with a link to the build log for the developer rather than for them.

Nobody has to remember to check the host’s dashboard, because the tool that said “publish” is the tool that says “live”.

What we give up for this

Closing the gap has costs and they are real.

Publishing takes one to three minutes rather than being instant, because there is genuinely a build. We show the progress rather than pretending otherwise, but it is slower than a server-rendered CMS where a save is immediately visible.

We hold a credential per site. That is a security surface: encrypted at rest with a dedicated key, never returned by any endpoint after entry, revocable at any time. But it exists, and a CMS that only fires an anonymous hook does not carry it.

We are opinionated about hosts. Vercel, Netlify, Cloudflare Pages, GitHub Actions, or an agent on your own server. Something exotic means the generic hook, which triggers builds without tracking them, and you are back to not knowing.

Grouping means a small delay. Publishes within a short window are batched into one build, so the very first click is not always instant.

Rolling back, which is the same problem in reverse

Everything above is about getting a change out. The mirror question is getting one back, and it is where the snapshot model earns its keep a second time.

With a mutable content state, reverting means editing the content back to what it was, from memory or from a version history, and triggering another build. That is a manual reconstruction, it takes as long as the original edit, and it is exactly the thing you do not want to be doing while a client is on the phone.

With immutable snapshots, every publication already is a restorable point. Reverting is selecting an earlier snapshot and rebuilding from it: the content state is not reconstructed, it is reused, byte for byte. Two clicks, one build, and the site is genuinely back to what it was at 11:04 this morning rather than to an approximation of it.

The constraint this creates is retention, and it is worth being explicit about because it is a real limit rather than a footnote. Snapshots occupy storage, so they expire: seven days on the free plan, ninety days on Site, a year on Studio. Beyond that window the snapshot is gone and reverting means editing by hand again.

There is one related behaviour worth knowing, because it surprises people: an image still referenced by a retained snapshot cannot be permanently deleted. If a client tries, they are told which snapshot holds it and when that snapshot expires. The alternative, letting the deletion through, would mean a rollback that restores a page with a broken image, which is worse than the inconvenience.

The question to ask any CMS

If you take one thing from this: when you evaluate a CMS for a static site, ask what the editor sees after they click Publish.

If the answer is “a confirmation that it saved”, you own the remaining six steps, and your client will find out about failures from their customers.

If the answer involves the words “live” and a time, someone has done the unglamorous work.

Our take on the rest of the architecture is in how we keep sites static, and the comparisons cover where each tool stops.

Back to the blog