QuickSEO
Pricing
Get Started
Documentation
  • MCP
  • AI Visibility metrics
  • Webhooks
View .md

Publishing webhooks

When you publish an AI-generated article in QuickSEO, we send it to your own endpoint as a JSON POST so it can land in your CMS or website automatically. Point the webhook at a small receiver on your side, verify the request, and create the post — no copy-pasting.

Introduction

A publishing webhook is configured per site. Once it's set up and enabled, clicking Publish on an article fires an article.published event to your URL with the full article body as both HTML and Markdown. Your endpoint creates (or updates) the post and returns a 2xx — at which point QuickSEO marks the article as Published.

Webhook publishing is available on paid plans.

Set up a webhook

  1. Open your site, then go to Settings → Webhook Publishing.
  2. Enter your Webhook URL (the endpoint that will receive the POST) and click Save.
  3. We generate a Bearer token for the site. It's shown in full only right after you save or regenerate it — reveal it with the eye icon and copy it somewhere safe. Afterwards only the last 4 characters are displayed.
  4. Make sure the Enabled switch is on.
  5. Click Send Test Article to fire a sample payload at your endpoint and confirm it responds with a 2xx.

You can Regenerate Token at any time (this immediately invalidates the old one — update your receiver) or Remove Webhook to disable delivery entirely.

The request

Each delivery is a single HTTPS POST to your configured URL:

MethodPOST
Content-Typeapplication/json
AuthorizationBearer <your-signing-token>
X-QuickSEO-Eventarticle.published
Timeout10 seconds per attempt

The Authorization header carries the site's signing token verbatim. Treat it as a shared secret: compare it against your stored copy to confirm the request really came from QuickSEO.

Payload

The request body is the same shape for real publishes and for the Send Test Article button (the test uses an all-zero id):

{
  "event": "article.published",
  "timestamp": "2026-06-05T12:34:56.000Z",
  "article": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "title": "How to Rank in ChatGPT",
    "slug": "how-to-rank-in-chatgpt",
    "description": "A practical guide to getting cited by AI assistants.",
    "tags": ["ai-seo", "chatgpt"],
    "cover_image_url": "https://cdn.quickseo.ai/covers/how-to-rank.png",
    "html": "<h1>How to Rank in ChatGPT</h1><p>…</p>",
    "markdown": "# How to Rank in ChatGPT\n\n…",
    "created_at": "2026-06-05T12:30:00.000Z"
  }
}

Fields

FieldTypeNotes
eventstringAlways article.published for now.
timestampstringISO 8601 time the delivery was sent.
article.idstringUUID. Use it as your idempotency key.
article.titlestringArticle title.
article.slugstringURL-safe slug.
article.descriptionstringMeta description / excerpt.
article.tagsstring[]May be empty.
article.cover_image_urlstring | nullAbsolute URL, or null if no cover.
article.htmlstringRendered HTML body. Use this for most CMSes.
article.markdownstringSame body as Markdown, if your CMS prefers it.
article.created_atstringISO 8601 creation time.

Verify the request

Reject anything whose Authorization Bearer token doesn't match the token you saved when configuring the webhook. Use a constant-time comparison to avoid timing leaks:

import crypto from 'node:crypto'

const QUICKSEO_TOKEN = process.env.QUICKSEO_WEBHOOK_TOKEN // the token you copied from Settings

export function handler(req, res) {
  const sent = (req.headers['authorization'] ?? '').replace(/^Bearer\s+/i, '')
  const expected = QUICKSEO_TOKEN

  const ok =
    sent.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sent), Buffer.from(expected))

  if (!ok) return res.status(401).json({ error: 'invalid token' })

  const { event, article } = req.body
  if (event === 'article.published') {
    // Upsert by article.id so retries don't create duplicates:
    //   create or update the post in your CMS using article.html (or article.markdown),
    //   article.title, article.slug, article.description, article.tags, article.cover_image_url
  }

  // Respond 2xx quickly — see "Responses & retries" below.
  return res.status(200).json({ ok: true })
}

Responses & retries

  • Respond with any 2xx to acknowledge delivery. On the first successful 2xx, QuickSEO marks the article as Published.

  • Respond fast. Each attempt times out after 10 seconds. If you have heavy work to do (image downloads, re-rendering), enqueue it and return 2xx immediately.

  • On failure — any non-2xx status, a timeout, or a network error — QuickSEO retries with backoff:

    AttemptWhen
    1immediately on Publish
    2~30 seconds after attempt 1 fails
    3~2 minutes after attempt 2 fails

    After 3 failed attempts we stop and the article is left unpublished. Every attempt (status code and your response body) is recorded so you can debug from the QuickSEO side.

  • Make your handler idempotent. Retries re-send the same article.id, so upsert by id (or slug) rather than blindly inserting.

Troubleshooting

  • Test says it failed with an HTTP status — your endpoint returned a non-2xx. The response body is shown inline in the Webhook Publishing card; check it for the reason.
  • Test says "Connection failed" / timeout — the URL isn't reachable from the public internet within 10s, or it's blocking the request. Confirm it's a public HTTPS URL and responds quickly.
  • 401 from your own receiver — the stored token and the one you're checking against have drifted. Re-copy the token from Settings, or Regenerate Token and update both sides.
  • Duplicate posts — you're inserting instead of upserting on retries. Key on article.id.

Need a hand wiring it up? Email support@quickseo.ai.

QuickSEO

HomeBlogTemplatesFree ToolsDocs

Tracking & Tools

Track ChatGPTTrack ClaudeTrack GeminiTrack Perplexity

Features

Search AnalyticsAI Visibility ScoresTracked PromptsPage GroupsBranded vs Non-BrandedCompetitor TrackingCited PagesMCP Server

Compare

vs Profoundvs Otterly.AIvs AthenaHQvs Peec AIvs Writesonic GEOvs Scrunchvs Evertunevs Bluefish AIvs Brandlightvs LLMrefsvs Mentions.sovs Superlines

Company

Terms of usePrivacy policysupport@quickseo.ai