Blog Automation Engine
One 86-node pipeline that researches, writes, illustrates and publishes a post for every client site on its own schedule — with a claim gate, a quality gate and a bounded retry.
- Client
- EpsolDev
- Category
- Automation Engineering
- Completed
- 2026-08-15
- Built with
- n8n, Google Vertex AI, REST API, WordPress
- Topics
- API Development, Automation, CMS
n8n 86 nodes Multi-tenant Runs every 6 hours
One workflow researches, writes, illustrates and publishes a blog post — for every client site on the platform, on each site's own schedule. It is the content engine behind Custom Blog Management.

Every image on this page is the running instance. Client domains and endpoint URLs are blurred — the architecture is the subject, not whose site it publishes to.
What it replaced
The same automation instance still holds six archived per-site blog workflows. That was the old shape: one client, one copy of the graph. A prompt improvement meant editing it six times, and the sixth copy was always the one that got missed.
This workflow is that graph written once and made tenant-aware. Onboarding a site is a configuration row. There is no seventh copy to drift.
| Per-site workflows | This pipeline | |
|---|---|---|
| Adding a client | duplicate ~80 nodes | one config row |
| Improving a prompt | edit every copy | edit once |
| Schedule | one trigger per site | one trigger, per-site due check |
| A site failing | that copy is broken | that iteration is logged, the rest continue |
How a run flows
Two entry points feed the same body — a 6-hourly schedule for normal operation, and a webhook for "publish this one site now".
Schedule ─┐
├─> Config ─> Dispatch ─┬─> one site (webhook)
Webhook ─┘ └─> sites_list (scheduled)
│
v
Loop Over Sites due? ─> claim ─> generate ───┘
publish, log
Every terminal branch — success, duplicate title, quality failure, missing topic, bad credentials — routes back into Loop Over Sites. One site failing can never end the run for the others.
The five decisions that make it survivable
1. A claim gate, because schedules overlap
Before any generation, the run calls back to the platform to claim that site:
CBM Claim Run -> Claim Gate -> Claim Granted?
│
false ───────┴──> back to the loop, silently
A 6-hour schedule and a manual webhook can fire within seconds of each other. Without the claim, both would spend a model call and publish two posts. The claim is a distributed lock held by the platform, not by the automation — so it holds even across a restart.
2. Every model call has two implementations
Research, title generation, body writing, title repair and image generation each sit behind a Use HTTP …? switch:
| Branch | Node | Why it exists |
|---|---|---|
| false | platform-native Vertex AI node | convenient, credential-managed, typed |
| true | plain HTTP request | provider-agnostic — routes to whatever the control plane picks |
This is a seam, not indecision The native node binds the graph to one vendor's credential model. The HTTP path lets the same workflow run against a different provider without a rewire — the control plane decides, the graph obeys. Five generative steps, five seams.
3. Duplicate titles loop back with exclusions
A generated title is checked against what the site already published:
Pick Title→CBM Dup Check→Dup Gate- not a duplicate → carry on to the body
- duplicate, retries left →
Add Exclusionsre-enters title generation with the rejected titles attached to the prompt - duplicate, retries exhausted → skip this site this cycle
The retry is not the same call again. It is a narrower call — the model is told what not to say.
4. A quality gate with a bounded retry and a logged give-up
Parse Blog -> Quality Gate -> Quality Failed?
│
true -> Gen Retry Available? -> Mark Gen Retry -> rewrite
└> Mark Gen Failed -> log -> next site
The give-up path is not a dead end: it writes the failure back to the platform (CBM Log Gen Failure) and then restores the loop item so iteration continues cleanly. A short or malformed draft is never published to make the graph look successful.
5. Images are optional at two independent points
A hero image and an in-content image are separately gated. Either can be disabled per site, and either can fail — and the post still publishes:
Image Gate → generate → Have Image? → upload → attach hero Content Image Gate → generate → Have Content Image? → upload → insert
Both gates have an explicit skip node (Mark Image Disabled, Mark Content Image Skipped) that rejoins the publish path. An image service outage degrades the post, it does not stop it.
Publishing and proof
Assemble Post → CBM Publish → Verify Publish → Build Run Log → CBM Run Log
Publication is verified, not assumed — the response is re-read before the run is recorded as successful. Every cycle writes a run log back to the platform, so the answer to "did the Tuesday 06:00 run do anything?" is a row, not a guess.
When it breaks

Two nodes, and it is the reason nobody watches a dashboard. n8n's error trigger fires on any unhandled failure in a linked workflow, and the handler posts the failure into the platform's own alert log — the same place operational alerts already live.
Handled failures are logged inline by the pipeline itself. This catches everything else.
Stack
n8n Google Vertex AI Imagen JavaScript REST API WordPress
Thirty-four of the 86 nodes are JavaScript Code nodes. The generative steps are model calls; the control — parsing, gating, deduplicating, restoring loop items — is plain code, because a graph that reads cleanly at 3am is worth more than a clever one.