Most “AI content” tools publish straight to your site. No review, no judgement, no human in the loop. That’s not a feature. That’s how you end up with a live article about something you’d never put your name to. We built it differently.
What it does
Every morning at 10:00, a pipeline wakes up, picks one topic from a queue, writes a full article draft, and emails it to the client. The email has two buttons: Approve and Request changes. Nothing touches the live site until the owner taps one of them. Thats the whole point.
If they approve, the article is committed to the site’s code repository. The host sees the new commit and deploys automatically. The Google Sheet row flips to “Live”. Done. If they want changes, the AI revises and the loop repeats until it’s right.
The stack - built in an afternoon
No enterprise content platform. No monthly seat licence. The tools:
- n8n - the workflow engine that runs the whole thing
- Google Sheets - the topic queue
- Gmail - approval emails
- GitHub - where the article files live
- Netlify - deploys on every commit automatically
The client’s site is built in Astro and hosted on Netlify. That matters because Netlify’s git-based deploy means “publish” is just a file commit. No separate CMS API to authenticate. No publish endpoint to call. Push the file, the site updates.
The topic queue anyone can use
The owner adds article ideas to a plain Google Sheet. Columns: Topic, Status, Published Date. Status values are Queued, In Review, Live. The pipeline filters for the first row where Status is Queued, marks it In Review, then hands the topic to the AI.
No logins to a new tool. No training session needed. If you can use a spreadsheet, you can manage the content calendar.
What the approval email looks like
The email lands in a normal Gmail inbox. It contains the full article draft - headline, body, everything - so the owner can read it properly before deciding. The two buttons are just links to a webhook URL with a query parameter:
https://your-n8n-instance.com/webhook/article-approval?decision=approve&rowId=42
https://your-n8n-instance.com/webhook/article-approval?decision=changes&rowId=42
When the owner taps a button, their browser hits the webhook. n8n reads the decision parameter and routes accordingly. One gotcha we hit: if the webhook fires without a recognised value in that field (a stray click, a preview renderer fetching the URL), the workflow needs a fallback branch that does nothing rather than crashing or publishing. We added an explicit check:
// n8n Function node - guard before the publish branch
const decision = $input.first().json.query.decision;
if (decision !== 'approve' && decision !== 'changes') {
return []; // stop the execution cleanly
}
Publishing to GitHub - the create vs edit problem
GitHub’s API has one endpoint for file writes, but it behaves differently depending on whether the file already exists. For a new article, you omit the sha field. For an update to an existing file, you must include the current file’s sha or the API rejects the request with a 422.
First time through the build, the workflow only handled the create case. Revisions failed silently. The fix: before writing, always call the contents endpoint to check whether the file exists and grab its sha if it does.
GET https://api.github.com/repos/YOUR_ORG/YOUR_REPO/contents/src/content/articles/SLUG.md
Authorization: Bearer YOUR_GITHUB_TOKEN
If the response is a 200, extract .sha and include it in the PUT body. If it’s a 404, leave sha out entirely. That single conditional makes create and update work from the same node.
Why the approval gate is the feature, not the friction
It would be easy to remove the email step and publish straight from the AI. The pipeline would run faster. It would also mean the client has no idea what’s on their site until a customer mentions it.
The approval tap takes about thirty seconds. In return, the owner reads every piece before it goes live, catches anything off-brand, and stays in control of their own site. For a local trade business, that trust matters more than speed.
Dont mistake “automated” for “unsupervised”. The AI handles the drafting. The human handles the judgement. That’s the split that makes this worth running.
What you get at the end
One new local SEO article on the live site every working day. A topic queue the owner controls from a spreadsheet. A full revision loop if the draft isn’t right. And a clear record in the sheet of everything that’s been written, reviewed, and published.
Built in an afternoon. Runs unattended. Publishes nothing without a human saying yes.
If you want to see what a pipeline like this could look like for your site, start a conversation with us - we scope these after a chat, not before one.



