Docs / Publishing & integrations / Publish destinations
Create a publish destination
A publish destination is where Content Studio sends finished articles. Before anything can be published, the workspace needs at least one — the endpoint, the signing secret, and optionally the IndexNow details. This guide shows you how to create and manage them.
1 · What a publish destination is
Content Studio is deliberately destination-generic: the same article is delivered through one canonical, versioned payload (PublishPayload) to whichever destination you choose. That is why one "webhook" type covers your blog, your CMS, a headless backend, or an automation tool — they all receive the same signed JSON.
| Type | Who creates it | What it does |
|---|---|---|
webhook — “Webhook / Custom Endpoint” | You (the only type you can create) | POSTs the signed PublishPayload to your absolute http(s) URL. → Receive content via webhook |
own_site — “AEO Spotlight Site” | AEO Spotlight (seeded, internal) | Publishes into AEO Spotlight's own public sections. It appears in the list as “Managed by AEO Spotlight” and is read-only — it cannot be created, edited or deleted from your workspace. |
2 · Where destinations are managed
Destinations live on their own page, Publish destinations — reached from your account menu (top right), or from the link under the destination selector in the Content Studio publish step. Configuring a target is configuration, not authoring, so it sits with Connected data (also in the account menu) rather than inside the writing surface.
3 · Create a destination
Open your account menu (top right) → Publish destinations.
Click New destination.
Fill in the fields below. Give it a name your team will recognise — with several destinations, the name is what you pick from in the publish step.
Click Create destination. It is available in the Content Studio publish step immediately.
| Field | Required | Notes |
|---|---|---|
| Name | ✓ | A label you'll recognise, e.g. “Acme blog receiver” or the client's name. |
| Endpoint URL | ✓ for webhooks | Your receiver's absolute http(s) URL. See §4 for the public-host rule. |
| Signing secret | Recommended | The shared key used to sign every delivery. Always set one. See §5. |
| IndexNow host | Optional | The exact host to ping on publish, e.g. www.example.com. See §6. |
| IndexNow key | Required if a host is set | Must be hosted at that host's root as /your-key.txt. A key only validates on the host it is hosted on. |
| Active | — | Inactive destinations stay in the list but are not selectable in the publish step. Use this to park a target instead of deleting it. |
4 · Endpoint requirements
- The URL must be absolute and use
httporhttps— a bare hostname likeapi.example.com/hookis rejected before saving. - It must resolve to a public host. AEO Spotlight SSRF-guards every delivery and refuses private, loopback, link-local and CGNAT addresses, so it can never be used to probe your internal network.
- For local development, expose your receiver with a tunnel (ngrok, cloudflared) and use the tunnel's public URL as the endpoint.
- Use
HTTPS. A secret-signed payload over plain HTTP is a credential leak. - One destination per site/CMS. Agencies running client sites typically create one destination per client, named after the client.
www.example.com/api/… can be served by the marketing site's SPA (returning HTML with a 200), while the real API lives on api.example.com. Confirm the endpoint returns your receiver's JSON before saving. 5 · The signing secret
Every delivery is signed with an HMAC-SHA256 over the raw request body, sent in the X-AeoSpotlight-Signature: sha256=… header. Your receiver recomputes it and compares in constant time. The secret is the only thing standing between your endpoint and forged content, so it is treated as a credential.
- Write-only. After saving, the value is never shown again — not in the list, not in the edit form, not in any API response. All you can see is whether one is set.
- Blank on edit means “keep”. Editing a destination leaves the secret field empty; leaving it empty preserves the stored secret rather than clearing it.
- It must match your receiver exactly. A mismatch is the single most common integration failure and surfaces as
401 Invalid signature. - Blank on create means unsigned. If you genuinely don't want signing, leaving it empty sends unsigned requests — and your receiver should then reject them.
- You cannot recover it. If it is lost, set a new value on both sides (destination and receiver) together.
Generate a strong one:
openssl rand -hex 326 · IndexNow (optional)
With an IndexNow host and key set, the platform pings IndexNow right after a successful publish so Bing, Yandex, Seznam, Naver and other participating engines crawl the new URL immediately. Nothing else about publishing changes.
- The key file must be hosted at the absolute root of the exact host you ping —
https://www.example.com/your-key.txt— returning the key as plain text. - The host must match where the file is served. If you ping
www.example.com, host the file there; if you ping the apex, host it there. Pick one and be consistent. - An IndexNow key is per-host. A key that works for one domain does not authenticate another — generate a separate key per host.
- If IndexNow is unconfigured, publishing still succeeds; the publish panel simply reports “IndexNow not configured”.
Full walkthrough: Set up IndexNow.
7 · What happens at publish time
- In the Content Studio publish step you pick the destination from a dropdown of active destinations (plus an optional author/byline for that delivery).
- Publishing delivers the article synchronously, single-attempt, with a 30-second timeout.
- A 2xx response succeeds: the live URL is recorded on the opportunity, IndexNow is pinged (if configured), and the piece moves to
published— after which AI citation monitoring is what closes the loop. - A non-2xx or timeout fails the publish and states the reason. Nothing is marked published, and there is no automatic retry — fix your receiver and publish again. Keep the receiver idempotent by slug so a retry cannot duplicate a page.
- Re-publishing an unchanged live article is deliberately blocked; a live page is changed through Refresh (then Publish update) or a variant at its own URL instead.
Receiver-side contract: Receive content via webhook.
8 · Editing, disabling and deleting
- Edit — change the name, endpoint, or IndexNow details. The secret stays put unless you type a new one.
- Disable — keeps the record (and its secret) but removes it from the publish dropdown. Prefer this over deleting when the endpoint is temporarily down.
- Delete — permanent, and confirmed first. Articles already delivered through it are unaffected, but the stored secret cannot be read back, so re-adding the destination means re-entering the secret on both sides.
9 · Troubleshooting
| Symptom | Cause & fix |
|---|---|
| Publish step shows “Choose a destination” and publish does nothing | No active destination exists. Create one here, or re-enable a disabled one. |
401 Invalid signature from your receiver | The secret on the destination and the one your receiver verifies with differ. Set the same value on both, and remember to re-sign over the raw body bytes. |
500 “webhook secret is not configured” from a self-hosted receiver | Your own receiver is missing its secret in its configuration — a receiver-side problem, not the destination's. |
| Save rejected: “absolute http(s) URL” | The endpoint isn't an absolute URL with a scheme. Add https://. |
| Delivery fails with an endpoint/host error | The host did not resolve to a public address, or the path is served by the wrong host (e.g. an SPA returning HTML). See §4. |
| Publish reports success but the live URL points at the webhook endpoint | Your receiver answered 2xx without a recognised URL field. Return url (or a Location header) — see the webhook guide's success section. |
| “IndexNow not configured” on the publish panel | No IndexNow host/key on the destination, or no destination IndexNow at all. Optional — publishing still works. |
