Docs
ESC
Sign in Platform API
GETTING STARTED
What QuQi is Add your website Business, brand and website settings Finding your way around Team and roles
THE CREW
Meet the crew Set up a teammate The calendar Chats and hand-offs Approvals and auto-approve Notifications
TEAMMATES
QuQi, the lead SEO Auditor Page Optimizer AI Visibility Copywriter Social Copywriter Link Exchange Video Producer Image Designer
WORKSPACES
Studio Image library
INTEGRATIONS
Integrations Connect WordPress Connect Shopify Connect social channels Telegram & WhatsApp Connect Ghost Connect Webflow Connect Wix Connect Notion Send articles to your own website Google Search Console Connect QuQi Edge
PLANS & USAGE
Plans Usage and credits Buying more Lifetime deal
TROUBLESHOOTING
Nothing was published The approval note never arrived A batch did not run You hit a limit

Send articles to your own website

For a site QuQi cannot publish to directly. Each finished article is handed to an address you give us, and your website takes it from there.

4 min read All plans

Most websites run on something QuQi already knows how to publish to: WordPress, Shopify, Webflow, Wix, Ghost or Notion. If yours is one of those, connect it from its own card and skip this page. The Webhook card is for everything else: a site your own developer built, where nobody but your code knows how to add an article.

Connected, it works like any other destination. When an article is finished, QuQi delivers it to the address you gave, and your site shows it however your developer decided. Like every publishing destination, it takes your one publishing slot.

START HERE You will need whoever built your website for one step: giving you an address that accepts articles. Everything else on this page you can do yourself. The last section is written for them.

Is this for you?

YOUR WEBSITE IS…USE
WordPress, Shopify, Webflow, Wix, Ghost or Notion That platform’s own card. Faster to set up, and QuQi can update an article in place afterwards.
Built by a developer (Next.js, Laravel, a static site, anything custom) Webhook, this page.
A site you cannot change at all Neither. Write with QuQi and copy each article out by hand from its page.

What you need

The drawer asks for two things. Neither comes from a third-party account; both are yours.

  • An address for articles. Your developer gives you this. It looks like https://example.com/hooks/quqi, and it has to be live on the public internet before you connect: QuQi sends a real test message to it the moment you press Connect, and only saves the connection if the address answers.
  • A password for the messages, called the signing secret. Optional. It lets your website check that a message really came from QuQi and not from somebody else. Type one and give the same one to your developer. Leave it blank and messages go out unsigned, which is fine for a site that does not check them.

Connect it

1
Open Integrations
Make sure you are on the right website, then find Webhook under Publishing and press Set up.
2
Paste the address
Every article for this website goes to that one address.
3
Type a signing secret, or leave it blank
Type one if your developer will check messages; whatever you type here is what they need. Blank sends unsigned.
4
Press Connect
The button reads Verifying… while the test message is on its way. If the address refuses it, nothing is saved and you are told what came back.
5
That is the whole thing
There is no blog, board or database to choose afterwards. The drawer closes and you get “Webhook connected.”

What happens after

From now on a finished article goes to your address in two ways: the Copywriter sends it when it publishes, exactly as it would to WordPress, or you open a written article and press Send to webhook yourself. You get “Sent to your webhook.” once your site has accepted it.

If your developer set the site up to reply with the article’s new web address, QuQi keeps that as the article’s link, so View live works from the article’s page. If they did not, the article still counts as published; there is just no link to follow.

GOOD TO KNOW The button always reads Send to webhook, never “Update on …”. That wording is for WordPress, WordPress.com and Shopify, where QuQi holds the remote post and edits it in place. With a webhook, QuQi hands your site the article and your site decides what to do with it.

Editing an article

Edit an article and send it again, and your site receives it again with the new text. QuQi does not tell your site “this is an edit”; it sends the same kind of message each time, with the same article id. Your developer matches on that id and updates the article they already have instead of adding a second copy. Ask them to confirm this before the Copywriter publishes on its own, because a site that adds a new article on every message will show a duplicate every time you edit.

Checking it worked

1
Look at the Integrations page
The card shows a green Connected badge with your address printed underneath, and the buttons become Reconfigure and Disconnect.
2
Send one article by hand
Open a written article and press Send to webhook. You get “Sent to your webhook.” once your site has accepted it.
3
Look at your website
The article should be there, with its title, text and cover image, before you leave the Copywriter’s auto-approve on.

One destination at a time

A website publishes to exactly one place. While WordPress, Ghost, Webflow, Shopify or another destination is connected, the Webhook card carries a lock and an “in use” note instead of a Set up button, and connecting is refused until you disconnect that one first.

Disconnecting asks you to confirm, then forgets the address. There is nothing to cancel at your end, and QuQi never reads anything from your site beyond its answer to each message.

CAREFUL A secret is never shown back to you after you save it. Reconfigure opens the box empty, and leaving it empty keeps the secret you already have. Typing a new one replaces it, so give the new one to your developer at the same time.

For your developer

Hand this section to whoever built the site. Everything above is all the owner needs.

Every message is an HTTP POST with a JSON body. Two headers tell you what you are looking at: X-Quqi-Event says ping for the connection test and article.published for a real article, and, when the owner set a signing secret, X-Quqi-Signature carries an HMAC-SHA256 of the exact body received, keyed with that secret, written as lowercase hex. Without a secret the header is absent. The ping is signed the same way, so one code path verifies both.

MESSAGEWHEN IT IS SENTWHAT IT CARRIES
ping The moment the owner presses Connect, and again on every reconfigure. The event name, the website’s domain, and the time it was sent.
article.published When someone sends an article by hand, or the Copywriter publishes one. The article’s id, title, slug, excerpt, full HTML, cover image, meta title and description, keyword, word count and publish time, plus the website’s id, domain and name.

What the endpoint has to do

  • Answer the ping with any 2xx. Anything else and the connection is refused, and the owner is shown the status that came back.
  • Answer within 15 seconds at connect time and within 30 seconds for an article.
  • Be a public address. Localhost, private ranges and names that do not resolve are refused before anything is sent. Up to three redirects are followed, each to a public address.
  • If a secret is set, verify the signature over the raw bytes, before parsing. Re-serialising the JSON first changes the bytes and the signature will never match.
  • Treat article.published as an upsert keyed on article.id, which is generated once and never changes. The slug is stable too if that suits your storage better. There is no separate update event.
  • Reply with a 2xx. A JSON body holding a url is stored as the article’s link in QuQi, and an id in the same reply is kept as your own reference. An empty 2xx still counts as delivered.

The article payload

Ids are 36-character strings. content is HTML, ready to drop into a page; there is no Markdown field. cover_image, excerpt, meta_title, meta_description and keyword can each be empty, and the cover image is passed on exactly as QuQi holds it, so do not assume it is a full web address.

ARTICLE.PUBLISHED
{
  "event": "article.published",
  "article": {
    "id": "9f1c2a7e-4b30-4c11-9a6d-2f5c8e1b7d04",
    "title": "How to keep a sourdough starter alive",
    "slug": "how-to-keep-a-sourdough-starter-alive",
    "excerpt": "A starter only needs three things…",
    "content": "<h2>Feeding<\/h2><p>A starter only needs…<\/p>",
    "cover_image": "https:\/\/cdn.quqi.io\/covers\/sourdough.jpg",
    "meta_title": "How to keep a sourdough starter alive",
    "meta_description": "Feed it, watch it, and know when it is done.",
    "keyword": "sourdough starter care",
    "word_count": 1480,
    "published_at": "2026-08-25T09:06:44+00:00"
  },
  "website": {
    "id": "3b7e5d10-8c22-4f91-b0a4-1d6e2c9f7a55",
    "domain": "example.com",
    "name": "Example Bakery"
  }
}

A receiver in Node

NODE
import crypto from "crypto";

\/\/ the signature covers the RAW body — parse only after checking it
app.post("\/hooks\/quqi", express.raw({ type: "application\/json" }), (req, res) => {
  const expected = crypto
    .createHmac("sha256", process.env.QUQI_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");

  const sent = req.get("X-Quqi-Signature") || "";
  if (sent.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(sent), Buffer.from(expected))) {
    return res.sendStatus(401);
  }

  const body = JSON.parse(req.body);
  if (body.event === "ping") return res.sendStatus(200);

  \/\/ same event whether it is the first send or an edit: match, then upsert
  const a = body.article;
  upsertPostById(a.id, a);

  res.json({ url: `https:\/\/example.com\/blog\/${a.slug}` });
});
Did this page answer your question? Yes Not quite