AutoBlog

Documentation

Integrations and API

Connect AutoBlog to your blog, CMS, or any HTTP endpoint. Delivery is one webhook — here is the exact contract.

How integration works

AutoBlog is the writer; your site is the publisher. On schedule, AutoBlog generates an article and POSTs it as JSON to a single webhook URL you control. Your endpoint stores the article (or forwards it to your CMS) and returns a 2xx. That's the whole contract — there is no polling and no SDK.

  • You set one webhook URL per campaign in the dashboard.
  • You may add a custom auth header (name + value) that AutoBlog sends on every request.
  • AutoBlog POSTs a JSON body with event "article.created".
  • Your endpoint stores or publishes the article and returns 2xx within 15 seconds.
  • Non-2xx or timeout: AutoBlog marks it failed and you can resend from Content.

What your site must add

Your receiving site needs three things: a route that accepts POST JSON, a place to store the article, and a page that renders it once stored. From there, published articles are just rows in your database. Below is a complete minimal receiver.

import express from 'express';
const app = express();
app.use(express.json());

app.post('/api/blog/webhook', async (req, res) => {
  // 1. Verify the shared secret header you configured in AutoBlog.
  if (req.headers['x-blog-secret'] !== process.env.BLOG_WEBHOOK_SECRET) {
    return res.status(401).json({ error: 'unauthorized' });
  }

  const { event, post } = req.body;

  // 2. Remove on unpublish; otherwise upsert the article (idempotent on id).
  if (event === 'article.removed') {
    await db.posts.deleteOne({ id: post.id });
    return res.json({ ok: true });
  }
  if (event !== 'article.created') return res.status(400).end();

  await db.posts.updateOne(
    { id: post.id },
    { $set: {
        id: post.id,
        slug: post.slug,
        title: post.title,
        excerpt: post.excerpt,
        body: post.body_html,
        category: post.category,
        tags: post.tags,
        date: new Date(post.date)
      } },
    { upsert: true }
  );

  // 3. Respond 2xx fast (AutoBlog times out after 15s).
  res.json({ ok: true });
});

app.listen(7007);

The payload

Every delivery is a single JSON object. body_html is ready-to-render HTML — sanitize it if you allow untrusted input, but it is generated by AutoBlog and safe to render. image_url is a hosted featured image (may be null if generation is disabled); image_alt is its alt text.

{
  "event": "article.created",
  "project_id": "65f2…ab9",
  "post": {
    "title": "How vector search works in 2026",
    "slug": "how-vector-search-works-in-2026",
    "excerpt": "Two to three sentence summary.",
    "body_html": "<h2>Section</h2><p>Full article HTML…</p>",
    "image_url": "https://autoblog.neoned71.com/images/post-…jpg",
    "image_alt": "Featured image alt text",
    "tags": ["vector-search", "databases", "ai"],
    "category": "tech",
    "date": "2026-09-15T08:00:00.000Z",
    "reading_minutes": 6,
    "sources": [{ "title": "Source name", "url": "https://…" }],
    "keywords": ["vector search", "embeddings"]
  }
}

Authenticating deliveries

Configure a header name and value in the campaign settings. AutoBlog sends that exact header on every POST, so you can reject anything that does not match. A dedicated shared secret is better than reusing an API key.

// Header name:    x-blog-secret
// Header value:   <a long random string>

if (req.get('x-blog-secret') !== process.env.BLOG_WEBHOOK_SECRET) {
  return res.sendStatus(401);
}

Idempotency & retries

Delivery can be retried (manually from Content, or after a transient failure), so treat post.id as the unique key and upsert. Never create a second row for the same id. Return a 2xx as soon as the write is durable. If you kick off a slow rebuild, do it in the background and still return 200 — AutoBlog gives you 15 seconds.

// Idempotency keyed on the post id: repeat deliveries overwrite, never duplicate.
await db.posts.replaceOne({ id: post.id }, doc, { upsert: true });

Rendering articles on your site

Once stored, a post is plain data: id, title, excerpt, category, tags, date, and body (HTML). Render it however your stack builds pages. You only need to read your own database — AutoBlog is not in the read path.

// Example route: /blog/[id]
const post = await db.posts.findOne({ id });
if (!post) return notFound();
return render(post.title, post.excerpt, post.body, post.date);

Publish & unpublish

Each article has Publish and Unpublish controls in AutoBlog. Publishing sends (or re-sends) the article to your webhook. Unpublishing removes it from your site: AutoBlog POSTs an "article.removed" event with the post id, and your endpoint deletes that post. Handle both events.

// article.removed payload
{
  "event": "article.removed",
  "project_id": "65f2…ab9",
  "post_id": "6ac0…19",
  "post": { "id": "6ac0…19", "slug": "how-vector-search-works" }
}

// In your receiver:
if (event === 'article.removed') {
  await db.posts.deleteOne({ id: post.id });
  return res.json({ ok: true });
}

Approval mode

If you want to review before anything reaches your site, turn on "Require approval before sending" on the campaign. AutoBlog then holds each article in the dashboard as pending; nothing is POSTed until you Publish. Rejected articles are never sent. Your endpoint does not change — it simply receives fewer, already-approved deliveries.

CMS shortcuts

If you do not want to build a receiver, point the webhook at a service that already accepts JSON:

Ghost

Create a Custom Integration in Ghost Admin, then have a small endpoint create a post via the Content/Admin API from the payload fields.

WordPress

Point the webhook at your own endpoint that calls the REST API (wp-json/wp/v2/posts) with title, content and tags, using an application password.

Zapier / Make

Use a webhook trigger, then map the payload fields into a CMS or a Google Doc for review.

Static sites

Receive the POST on a serverless function, write the post into your content store, then trigger your host’s deploy hook to rebuild.

REST API

Manage projects, keys, and webhooks programmatically with your API key as a bearer token.

curl -X POST https://autoblog.neoned71.com/api/projects \
  -H "Authorization: Bearer ab_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": "My Blog", "topics": ["ai", "tech"], "frequency": "2x" }'