Connect your site to SiteOps

Every other connection in SiteOps exists to bring numbers in. This one carries content out. The publishing webhook is one small address on your own site where SiteOps delivers finished work — blog articles from the planner, promo pages for seasonal moments, marketing messages — and your site puts each piece live under your own domain.

The contract is deliberately small. For every piece of content, SiteOps sends one POST request to your address. It carries five fields — the type (a post or a page), the slug it should live at, the title, the ready-made html, and when it should be visible — and it is signed with a secret only you and SiteOps hold. Your site checks the signature, creates the page, and answers that it is done. SiteOps then watches your public site until the page is really live, and tells the search engines about it: IndexNow for Bing and its partners, and Google.

It is the mirror image of the Push API. The Push API is your server talking to SiteOps — your numbers coming in. This webhook is SiteOps talking to your server — finished content going out. Set it up once, in about half an hour with whoever maintains your site, and everything SiteOps produces gains a road to your domain. Only content that passed your review ever travels on it.

SiteOps delivers, your site answers

Quick links

What SiteOps reads

  • This connection mostly writes: SiteOps sends the content you approved — the five fields above — and nothing else. No tracking code, no plugins, no changes to pages it did not create.
  • It reads back only the public page, the same way any visitor's browser would, to confirm the piece is live before telling the search engines about it.
  • Nothing travels without passing your review rules. At low autopilot levels that is you approving each piece; at higher levels it is the trust you explicitly granted that content type — ads and payments are never part of this contract.
  • Payments stay untouched everywhere. SiteOps schedules content — pages, posts, marketing emails — and never writes to a billing tool or weaves offers into your copy.
  • Removing the webhook stops all publishing to your site immediately. Pages already published are yours — they live on your domain and stay there.

Set it up, step by step

0 of 9 done

    1. Settings
    2. Publishing
    1. Calendar
    1. Settings
    2. Publishing

Let an agent do it

This is the most agent-friendly setup in SiteOps: an agent with access to your site's code can build the whole endpoint for you.

Prompt for your agent
Implement the SiteOps publishing webhook on my site (stack: <e.g. Next.js app router, WordPress, Rails>).

1. Add a POST route at /siteops-webhook on the live domain, served over https.
2. It receives JSON with five fields: type ("post" or "page"), slug, title, html, publish_at.
3. Verify the signature header against the raw request body using the whsec_ secret from the environment variable SITEOPS_WEBHOOK_SECRET. Compare before any JSON parsing — re-serialized JSON breaks the match — and reject mismatches with a 401 without creating anything.
4. Create or update the content at exactly the slug sent. Treat the slug as the identity: a repeated delivery updates the existing piece instead of duplicating it.
5. Answer with success within a few seconds. If building the page is slow, store first, answer, then build in the background.
6. Wrap the html in the site's own layout — header, footer, styles — like any post written by hand.

You still paste the endpoint URL into SiteOps settings, copy the whsec_ secret it shows once into your site's configuration, and approve every piece of content that travels.

If something does not work

My endpoint rejects every request with a signature mismatch
Three usual causes, in order. Your code verifies a re-serialized copy of the JSON instead of the raw bytes it received — verify before parsing. The secret in your configuration is stale — if anyone pressed Regenerate, the old whsec_ value is dead. Or a proxy in front of your site rewrites the body — compression and automatic JSON formatting both break the signature. Compare the exact bytes against the current secret and it matches.
I set it up on our staging site and nothing ever goes live
SiteOps confirms a page live by fetching its public address on the domain your project points at. A staging endpoint accepts the content happily, but the page appears on the wrong domain — so confirmation never succeeds and the item sits at confirming forever. Move the endpoint to the live site; if you want a safe first run, use a small unlisted piece rather than a staging copy.
Deliveries time out even though the page does get created
SiteOps waits a handful of seconds for your answer, then marks the delivery failed and retries. If your endpoint triggers a full site rebuild before responding, the answer arrives too late even though the page eventually appears. Flip the order: store the content, answer success, then build. The live-confirmation step tolerates a slow build — the answer step does not.
The item says confirming, but the page has been live for an hour
Check the address, not the page. SiteOps looks for the page at the exact slug it sent; if your CMS cleaned the slug up — stripped a word, swapped a character, added a date prefix — the page is live somewhere SiteOps is not looking. Either create the page at the slug exactly as sent, or return the final address in your success response so SiteOps knows where to check.
A retry created the same post twice
Your endpoint treats every delivery as brand new. Make the slug the identity: before creating, check whether a piece at that slug already exists, and update it if so. Once repeats are updates, retries become invisible — and re-sending an old piece simply refreshes it.
Is this the same thing as the Push API key?
No, and the two do not interchange. The Push API key (it starts with sk_push_) lets your server write numbers into SiteOps. The webhook secret (it starts with whsec_) lets SiteOps write content to your site. Opposite directions, different credentials — a push key pasted into your webhook configuration verifies nothing, and the other way round.

Set up once, see everything every morning

Get started