Connect Webhook to AltoRank

Webhook authenticates with a your endpoint, optionally signed that you create and can revoke yourself. Connecting it makes Webhook an available destination — it does not give AltoRank permission to publish. That still takes a person approving the draft.

What you will need

Webhook URL
An https:// endpoint you control
Signing secret
Optional
When set, every request carries an HMAC-SHA256 signature of the body so your endpoint can verify it came from us
Also accepted by the connector, not asked for by the form (1)
headers
Additional headers sent with every request, for example your own bearer token. Accepted by the connector; not in the connect form yet

These keys are in the connection schema and work when a connection is created from your own code or on a self-hosted install. They are listed so the docs match the code, not because you need them.

Setup

  1. 1 Create the credential in Webhook

    Go to Wherever you run it. AltoRank never asks for your Webhook login: you create a scoped credential there and paste it here, so revoking our access later is one click on your side and does not change how you sign in.

  2. 2 Add the connection in AltoRank

    In your workspace, open Integrations, choose Webhook, and fill in webhook url. The connection is tested before it saves, so a wrong value fails here rather than silently at publish time.

  3. 3 Approve an article

    Nothing publishes on connection. Open a draft, read the fact-check verdict, and approve it. Only then does AltoRank call Webhook. You can also turn on automatic publishing for the workspace, which ships after a hold you can veto; either way there is no agent route around the approval.

What approving does in Webhook

On approve
POSTs a JSON body with action "publish" and the article. Any 2xx response counts as published; an id and url in the response are recorded as the published id and URL.
On unpublish
POSTs action "unpublish" with the id your endpoint returned.

The requests your endpoint receives

Three actions, one endpoint, always POST with a JSON body. The article ships as rendered HTML with its slug and approved meta description; there is no separate Markdown field. tags is included only when the article carries tags.

publish — sent when a person approves a draft

POST <your endpoint>
Content-Type: application/json
X-Webhook-Signature: sha256=<hex HMAC-SHA256 of the raw body>   (only when a signing secret is set)
<your extra headers, if configured>

{
  "action": "publish",
  "article": {
    "title": "The article title",
    "html": "<h2>...</h2><p>...</p>",
    "slug": "the-article-slug",
    "metaDescription": "The approved meta description",
    "publishedAt": "2026-09-05T08:00:00.000Z"
  }
}

unpublish — sent when an editor unpublishes

{ "action": "unpublish", "externalId": "<the id your endpoint returned on publish>" }

test — sent once when you connect; the connection saves only on a 2xx

{ "action": "test" }

What to return

Any 2xx status counts as published. A JSON body is optional; when present, id (or externalId) is stored and sent back to you on unpublish, and url (or link) is recorded as where the article lives. Return 2xx only once you have stored the article: there is no automatic retry, and a non-2xx marks the publish as failed in the log so an editor can retry it from the article.

HTTP 200
Content-Type: application/json

{ "id": "your-record-id", "url": "https://example.com/blog/the-article-slug" }

Verifying the signature

With a signing secret set, every request carries X-Webhook-Signature: the string sha256= followed by the hex HMAC-SHA256 of the raw request body. Compute it over the bytes you received, before parsing, and compare in constant time.

import { createHmac, timingSafeEqual } from 'node:crypto';

const expected = 'sha256=' + createHmac('sha256', process.env.SIGNING_SECRET).update(rawBody).digest('hex');
const given = req.headers['x-webhook-signature'] ?? '';
const ok = given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));

Worth knowing

  • The escape hatch: if your stack is not in the list, this is how you publish to it.
  • Nothing about the approval gate changes. The webhook fires when the draft is approved, by a person or by the rule they set, not when it is generated.
  • Delivery is one attempt. A non-2xx response marks the publish as failed in the publish log and it can be retried from the article; there is no automatic retry, so return 2xx only once you have stored the article.

The adapter behind this page is apps/web/lib/cms/webhook.ts . If this page and that file disagree, the file is right and the page has a bug.

Common questions

Does AltoRank publish to Webhook automatically?

Only after a person approves the specific draft. Connecting Webhook removes the copy-and-paste step; it does not hand over the decision. The publish path requires an approval recorded under a person, by click or by the rule they set, and the MCP server exposes no publish tool, so this holds even when an AI agent is driving the product.

What Webhook credentials does AltoRank need?

Webhook URL, Signing secret (optional). Your endpoint, optionally signed. Nothing else, and never your Webhook account password.

Can I publish to Webhook as a draft instead of live?

Not from AltoRank: the review happens here, before anything is sent, so approving is the act of publishing. On approve, POSTs a JSON body with action "publish" and the article. Any 2xx response counts as published; an id and url in the response are recorded as the published id and URL. If you want a staging destination, connect Notion as well and approve there first. Unpublishing is available afterwards: POSTs action "unpublish" with the id your endpoint returned.

Can I revoke access without breaking my site?

Yes. The credential is created in Webhook and revoked there, independently of your login. Revoking it stops AltoRank publishing and changes nothing else. Articles already published stay where they are: they are ordinary content on your platform, not embeds pointed at us.

Can I use Webhook on the self-hosted version?

Yes. Self-hosting is the same product under an open-source licence with no feature gates, so every connector works and your credentials never leave your own infrastructure. The adapter is apps/web/lib/cms/webhook.ts in the repository.

Be the site the assistant names.

Add a domain and the first draft is written while you watch. Every publish is your decision, a click on the draft or a rule you set and can hold, and you can read the source or self-host it free.

Add a domain, it sets up your workspace