integration · push webhook

One signed POST per article, to any endpoint you host

No plugin, no polling. When an article passes the checks, the engine POSTs it to the URL you registered, signed with an HMAC over the exact bytes. Verify, store, return 200. Takedowns arrive on the same wire as event delete.

available now

Set it up yourself in a few minutes.

Connect Any site (push webhook)

reviewed 2026-09-02 · by the IT Master editorial team · how we check facts

setup

  1. 01Ask us to register your site with connector push, giving the HTTPS URL that will receive articles and a shared secret; we confirm once it is stored, and the secret is never displayed again, so keep your own copy.
  2. 02Add a POST route at that URL and read the raw request body before any JSON middleware touches it.
  3. 03Recompute a hex HMAC-SHA256 of those bytes with the secret and compare it constant-time against the X-Thoth-Signature header; answer 401 on a mismatch, 400 on unparseable JSON.
  4. 04Parse the body into event and article, and branch: upsert means insert-or-replace keyed on the article slug, delete means remove the page or serve a 410.
  5. 05Compare the payload's content hash with the one you stored last time and skip the write when they match, so an at-least-once retry becomes a no-op.
  6. 06Map the payload fields you need: body_html, meta_description, canonical_path, jsonld, hero_image, faq, tags and topic_cluster all arrive ready to use.
  7. 07Invalidate the cache for the article path, your blog index and your sitemap before returning, then answer 200 quickly and do any slow work afterwards.
  8. 08On Next.js, skip most of the above: export createWebhookRoute from the SDK with your secret and a revalidate closure.
  9. 09Publish a test article from the dashboard, confirm it lands, then take it down and confirm the delete event removes it.
  10. 10Schedule a reconcile against the pull feed so any delivery missed during a deploy or an outage is picked up automatically.

One POST per article, and three headers

When an article passes the validation checks, the engine sends a single POST to the URL you registered. The body is one JSON object with two keys: event, either upsert or delete, and article, the finished payload.

Three headers travel with it:

  • Content-Type: application/json
  • X-Thoth-Event — the event name again, so you can route before you parse
  • X-Thoth-Signature — a hex HMAC-SHA256 digest of the request body, keyed by your shared secret

The article object is the same one the pull feed serves, schema version 1.1: title, meta description, excerpt, the body as HTML and as Markdown, canonical path, a JSON-LD graph, hero image with alt text, FAQ pairs, table of contents, internal links, outbound citations, Open Graph and Twitter blocks, tags, topic cluster, hreflang and text direction, word count, reading time, author, version and a content hash.

Nothing has to be derived on your side. If your CMS wants a featured image, it is in the payload. If it wants schema, that is there too, built against the article's real author, links and questions rather than a template.

in one sentence

IT Master's push webhook sends one HTTPS POST per published article: a JSON body of {event, article}, signed with a hex HMAC-SHA256 of the exact bytes sent, in the X-Thoth-Signature header. Takedowns arrive on the same route as event delete.

Verify over the raw bytes, before you parse

The signature covers the exact bytes on the wire, so read the raw body and verify it before JSON parsing. Re-serialising the parsed object and signing that will not match: the engine serialises once, compactly, with no space after separators and with non-ASCII characters left as UTF-8, then signs and sends those same bytes.

Verification is a few lines in any language. HMAC-SHA256 the raw body with your secret, hex-encode it, compare constant-time against the header. Our SDK helper tolerates a leading sha256= prefix; the engine does not send one.

Two things matter beyond the maths. Keep the secret server-side, so the route never runs in a browser bundle. And do not accept unsigned requests while you are wiring it up, meaning to tighten it later. Until that check is in place your endpoint is a public, unauthenticated POST route, and the signature is the only thing between it and anyone else writing posts on your blog.

To rotate, ask us to store a new secret and swap it on your side at the same time. We can confirm that a secret is set, but the value is never displayed again once stored, so keep your own copy.

in one sentence

Verify a webhook over the raw request body, never over the parsed and re-serialised object: the sender signs the exact bytes it transmits, and any difference in spacing, key order or escaping will make a valid request fail its own signature check.

Two events: upsert and delete

upsert covers every content change. First publish, a nightly refresh of a decaying article, a rewritten title and meta description, corrected figures — each arrives as upsert with a bumped version and a new content hash. Treat it as insert-or-replace keyed on the slug.

delete is a takedown: an article withdrawn because a fact moved, a product was discontinued, or an editor pulled it. The payload's status is re-stamped to unpublished before it is sent, so a receiver keying on status rather than on the event reaches the same answer. Remove the page or serve a 410. Where the takedown named a replacement URL, the redirect is pushed to your site first, so a crawler arriving on the deindex ping meets the 301 instead of a 404.

The Next.js handler maps unpublish, unpublished, delete, deleted and takedown to one onDelete callback, and treats any article whose status is unpublished the same way. Prerendered stacks need the cache invalidation as well as the database write: a page built yesterday keeps serving until something tells it not to.

Delivery, retries, and what your 200 means

Any status below 400 counts as delivered. Anything else — a 500, a 404 from a mistyped path, a read timeout, a DNS failure — is a failed attempt, and the job is requeued with exponential backoff: two seconds, then four, then eight, then sixteen, under a five-minute ceiling, up to five attempts before the failure is dead-lettered and logged. Each attempt allows 20 seconds by default.

So delivery is at-least-once, not exactly-once, and your handler has to be idempotent. Every payload carries a version and a content hash; if the hash matches what you already stored, skip the write — no rewrite, no new revision, no touched modified date. Return 200 promptly and do slow work afterwards, or your own processing time becomes our retry timer.

Push never gates publishing. A failed POST is reported as data rather than raised as an error, so an endpoint that is down for an afternoon does not stall the queue behind it. Nothing is lost either: the articles it missed are still in the pull feed.

Next.js has a drop-in route; anything else is a short handler

On Next.js, install the SDK and export one handler. createWebhookRoute takes your secret, optional onUpsert and onDelete callbacks, and a revalidate closure over revalidatePath, so a publish invalidates the article and the blog index in the same request. It verifies with Web Crypto, which means it runs on the Edge runtime as well as Node, and it answers 401 on a bad signature, 400 on malformed JSON, and 200 with the event and slug otherwise. The wiring is on the Next.js page.

Everywhere else the handler is the same five moves in whatever language you use: read the raw body, verify, branch on the event, write, return 200. Laravel, Django, Rails, a Cloudflare Worker, a Lambda behind API Gateway — none of them need anything from us except the secret.

WordPress is the one place we steer people away from a webhook. Shared hosting and a stable public endpoint do not reliably coexist, so the plugin polls outbound instead. Same articles, arriving within fifteen minutes.

The pull feed is your backstop, and the same signature carries fixes

A webhook receiver is not a system of record. The pull feed is: an authenticated GET that returns articles newest-first with a cursor, so a site that missed a week walks forward from its last cursor and reconciles. Tombstones sit in that feed too, as items with status unpublished. Wire both — push for latency, the feed for truth — and a bad deploy costs you nothing. The feed is documented with the rest of the REST surface in the docs.

The same signing scheme carries the write side, on a separate route you switch on per site. Where it is enabled, the engine POSTs title, meta and JSON-LD patches, reviewed copy rewrites and revalidation requests to your endpoint, each attributed and signed identically, each answered with a per-patch result so partial success is visible. That is how a site with hundreds of existing pages gets them improved rather than only receiving new ones.

How it works covers everything upstream of delivery: the research, the writing, and the rival-vendor models that check the draft.

in one sentence

Nothing is pushed to a customer site until the draft has passed judges from a different vendor than the model that wrote it. A push webhook delivers finished, checked articles, not drafts awaiting review.

questions people ask

What do I actually have to build?

One HTTPS POST route. It reads the raw request body, recomputes an HMAC-SHA256 of those bytes with your shared secret, compares the hex digest constant-time against the X-Thoth-Signature header, then parses the JSON. On event upsert it inserts or replaces the article keyed on its slug; on event delete it removes the page. Return 200 and you are done. There is no handshake, no polling loop and no SDK requirement: any language that can hash bytes and parse JSON is enough.

Why must I verify the raw body rather than the parsed object?

Because the signature is computed over the exact bytes we send. The engine serialises the payload once, compactly, with UTF-8 characters preserved, signs those bytes and puts them on the wire. If you parse the JSON and re-serialise it, your language will almost certainly differ in spacing, key order or escaping, and the digest will not match a valid request. Capture the raw body first, verify, and only then parse. Every framework offers a way to read it before its JSON middleware runs.

What happens if my endpoint is down?

The delivery is retried with exponential backoff, starting at two seconds and doubling each time — four, eight, sixteen — under a five-minute ceiling, for up to five attempts. Each attempt waits 20 seconds for your response. After the last one, the failure is dead-lettered and logged rather than retried forever. Publishing is not blocked by any of this, and nothing is lost: every article that failed to reach you is still available from the pull feed, so a receiver that comes back walks forward from its last cursor and catches up.

How do I stop duplicate posts?

Delivery is at-least-once, so plan for a repeat. Key your records on the article slug and treat every upsert as an insert-or-replace rather than an insert. Each payload also carries a version number and a content hash; if the hash equals the one you stored last time, skip the write entirely, which avoids a pointless revision and a changed modified date that would otherwise show up in your sitemap. That single comparison makes replays free and turns a retried delivery into a no-op.

Can I run the webhook and the pull feed at the same time?

Yes, and it is the setup we recommend. The webhook gives you latency: an article reaches your site as soon as it publishes, rather than waiting for your next poll. The feed gives you truth: it lists everything published for your site, newest first, with a cursor to resume from and tombstones for anything withdrawn. Run a reconcile against the feed on a schedule, or after a deploy, and any delivery your endpoint missed while it was down or misconfigured is picked up without anyone chasing it.

Does using the webhook cost anything extra?

No. Delivery is part of publishing, whichever route you use: the plugin, the SDK, the REST feed or this webhook. You pay per published article from a prepaid balance, with no subscription, and a draft that fails the checks is not charged at the full rate. Retrying a delivery does not publish the article a second time, so an endpoint that was down for an afternoon costs you nothing beyond the time it takes to reconcile. See the pricing page for current rates.

See what search engines and AI assistants find on your site

Free, no account. Type your address and we show you what is missing and what we would write first.