Connecting your site and Google
Publish anywhere with a webhook
Send each finished post to Zapier, Make, n8n or your own server as signed JSON: what is sent, how to connect it, and how to check the signature.
Last checked against the product on
Blog AI publishes straight to WordPress and Strapi. For anything else, connect your site with a webhook: every time a post is published or updated, Blog AI sends the finished post as JSON to an address you choose. That address can be a Zapier zap, a Make scenario, an n8n workflow or your own server, and from there the post can go anywhere those tools can reach.
What Blog AI sends
Each delivery is an HTTPS POST with a JSON body. It carries the whole post: title, slug, excerpt, SEO title and meta description, the search query the post targets, categories and tags (categories holds each category's own name; category_paths holds subcategories in full, such as Guides › Payments), the body as both HTML and Markdown (with your call to action already in it), a link to the featured image with its size, the sources the post cites, the author you set (author: name, role or qualifications, short bio and profile link; see Show who wrote your posts) and any reviewed_by, and its status (publish, draft or scheduled). published_at never changes once a post is live; updated_at is when its text last changed (a refresh or Update live post).
There are three kinds of delivery, named in the event field and the X-BlogAI-Event header:
post.published: a post was published (or saved as a draft) for the first time.post.updated: a live post changed, for example after Update live post or an applied refresh. It includesremote_id, the id your system gave the post.connection.test: a small test, sent when you add the site and when you use Fix connection.
The post's key takeaways and FAQ are in the delivery twice: as sections of body_html and body_markdown (between blogai:takeaways and blogai:faq markers), and as data in key_takeaways and faq. structured_data holds the post's schema.org JSON-LD (null when the site's Structured data (schema) setting is off): print it in a <script type="application/ld+json"> tag on the post page. Blog AI never deletes or unpublishes anything through a webhook, and never changes a slug on its own: post.updated deliveries carry the slug the post was published with.
The featured image link keeps working for at least 7 days. Copy the image into your own system rather than linking to it.
Every field is described in a JSON Schema at https://blogaihq.com/webhooks/post.v1.schema.json. Field names are snake_case, the body has schema_version: 1, and new fields may be added to version 1 but none are removed or renamed.
Connect a webhook site
- Get your webhook address. Create it in Zapier, Make or your own server first (see the sections below). It must start with
https://. - Add the site. On Sites, click Add site and choose Webhook (Zapier, Make or your own endpoint).
- Fill in the details. Enter the site name, your website address, the webhook URL and, if you like, your blog address (for example
https://example.com/blog). Blog AI uses the blog address to link each post when your webhook doesn't send a link back. - Add the site and send a test. Blog AI creates the site, sends a
connection.testdelivery and shows whether your webhook answered. - Copy the signing secret. It is shown once, straight after connecting. Your server uses it to check that requests really come from Blog AI. If you lose it, create a new one under Fix connection.
From then on, publishing and scheduling work as they do for any site: see Publishing and scheduling posts.
Zapier
- In Zapier, create a zap with the trigger Webhooks by Zapier → Catch Hook, and copy the address it gives you.
- Connect the site in Blog AI with that address. The test delivery arrives as a sample, so Zapier can see every field.
- Add an action for the system your blog runs on, if Zapier has an app for it: for example a Webflow CMS item, a Ghost post or a Google Doc. Map
post.title,post.slug,post.body_html(orpost.body_markdown),post.meta_descriptionandpost.featured_image.urlto its fields. - Turn the zap on. Zapier answers every delivery with success, so Blog AI can't see what the next step did; check the zap history if a post doesn't appear.
Make
- In Make, start a scenario with Webhooks → Custom webhook and copy its address.
- Connect the site in Blog AI. The test delivery lets Make learn the data structure.
- Add the modules that create the post in your system and map the fields as above.
- To show the live link in Blog AI, end the scenario with a Webhook response module whose body is
{"id": "…", "url": "…"}.
Your own server
Answer with any 2xx status within 15 seconds to confirm the delivery. Anything else, including a redirect, counts as a failure: the post is marked as failed with the reason ("Your webhook answered 500") and you can retry it. Blog AI tries once and never retries on its own.
If your answer is JSON with an id and a url, Blog AI stores them: updates send the id back as remote_id, and the post shows its live link. Use delivery_id to ignore a delivery you have already handled.
Check the signature
Every delivery has an X-BlogAI-Timestamp header (Unix seconds) and an X-BlogAI-Signature header: sha256= followed by the hex HMAC-SHA256 of the timestamp, a full stop and the raw request body, keyed with your signing secret. In Node.js:
import crypto from 'node:crypto'
import express from 'express'
const app = express()
const SECRET = process.env.BLOGAI_SIGNING_SECRET
// Keep the raw body: the signature covers the exact bytes Blog AI sent.
app.post('/blog-ai', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.get('x-blogai-timestamp') ?? ''
const signature = req.get('x-blogai-signature') ?? ''
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(`${timestamp}.${req.body}`).digest('hex')
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300
const valid = signature.length === expected.length && crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
if (!fresh || !valid) return res.status(401).json({ error: 'bad signature' })
const delivery = JSON.parse(req.body)
if (delivery.event === 'connection.test') return res.json({ ok: true })
// Create or update the post here (delivery.post.remote_id is set on updates).
res.json({ id: 'your-post-id', url: 'https://example.com/blog/' + delivery.post.slug })
})
A new signing secret replaces the old one straight away, so update your server when you create one.
What webhook sites can't do
- Import. Blog AI can't read posts back from a webhook, so importing existing posts and pages isn't available. Your voice can still be set by describing it: see Set your writing voice.
- Unpublish or delete. Take a post down in the system your webhook publishes to.
- Check the live post before an update. WordPress and Strapi posts are compared with what Blog AI last sent, so edits made on your site are never overwritten. A webhook can't be read, so Update live post and refreshes send the whole post again and your system decides what to do with it.
Common problems and fixes
Every failed delivery marks the post as failed with the reason below, and you can click Retry publish. Blog AI sends once and never retries on its own.
"Your webhook answered 404 (not found)"
The address no longer exists. Zapier and Make hooks stop working when the zap or scenario is deleted or turned off. Turn it back on, or create a new hook and enter its address under Fix connection.
"Your webhook answered 401 (refused)" or "403"
Your server refused the request, usually because its signature check failed. Make sure it uses your current signing secret (a new secret replaces the old one at once) and checks the signature over the raw body, not re-encoded JSON.
"Your webhook didn't answer within 15 seconds"
Your endpoint did slow work before answering. Answer with a 2xx straight away and do the work afterwards (for example, in a queue). Then retry.
"Your webhook redirected to … (HTTP 301), so nothing was delivered"
Enter the final address under Fix connection. Redirects are never followed.
"Couldn't reach your webhook (…)"
The server is down, or the address has a typo. Check the address and that the server is up.
"Your webhook answered 429 (too many requests)"
Your endpoint or Zapier/Make plan is limiting requests. Wait a few minutes and retry, or upgrade the plan if you publish a lot at once.
"Your webhook answered 500: …"
Your endpoint failed. The text after the colon is what it said; check its logs (or the zap or scenario history).
"The webhook URL must start with https://"
Blog AI only sends to public https:// addresses. Addresses on your own computer or a private network are refused with "Blog AI can't send to this webhook URL".
The post arrives but looks wrong where it ends up
Map post.body_html to fields that take HTML, and post.body_markdown to fields that take Markdown. Map the title from post.title: it's plain text, so an "&" arrives as "&", not "&". If the receiving system shows "&", it is escaping the title itself.
The featured image is missing
Map post.featured_image.url and copy the file into your system: the link expires (see expires_at). featured_image is null when a post has no featured image.
The post link in Blog AI is wrong, or posts have no Google data
Blog AI links each post as your blog address plus the slug, unless your webhook answers with a url. If your system uses other addresses, answer with the real url, so Search Console data can be matched to the post.
Scheduled posts arrive a few minutes late
Scheduled posts wait in Blog AI and are sent within about five minutes of their time, in your computer's time zone.
See also Connection troubleshooting.
Next step
Next stepPublish or schedule a postPublish a post to WordPress or Strapi now, or schedule it. How scheduling differs between the two, what happens if it fails, and read-only sites.More about this part of Blog AI: Integrations.
Related articles
- Adding your siteAdd your WordPress or Strapi blog on the Sites page, how many sites each plan includes, and what read-only sites mean after a downgrade.
- Fix a broken connectionWhen publishing or importing stops working, use Fix connection to enter new WordPress or Strapi details without losing posts, ideas or data.
Was this article helpful?
Still stuck?
Tell us what you were trying to do and what happened, and we'll reply by email. Contact us.