Projects & Development · n8n
Automation, up close
Excerpts from the n8n workflows that run my projects on a Raspberry Pi. Each one is redrawn from the live workflow definition: same node names, same connections, trimmed to the part worth explaining. The numbers mark what a node is doing and why it is there.
- 51active workflows
- 83workflows in total
- 1error handler for all of them
- as of 3 Oct 2026
01 · Human approval gate
Nothing is published without a yes
An AI-written script should never reach video without a person reading it. The approval also has to finish: no endless revision loops, and no idea quietly regenerated after it was turned down.
Write Story Script
Gemini writes the script scene by scene with JSON output switched on, so the next step can check its structure instead of guessing at free text.
Script Approval Gate
Gmail send-and-wait. The email shows every scene, the estimated runtime (flagged if it's short of target) and a continuity flag, with a form: Approve, Request changes or Decline. It waits up to two days.
Route Decision 1
A switch on the answer. Only “Approve” leads onwards; there is no default route into production.
Revise Script
My notes go back to Gemini for one revision. The second form only offers Approve or Decline, so revisions stop after one round.
Mark Declined (1)
A declined script is marked on the content plan sheet, so the same idea isn't picked up and regenerated on the next run.
Save Script To Library
The approved script is saved to Drive and logged. If a later stage fails, the next run resumes from this approved copy instead of writing a new one.
Every published story has passed a person. When a render fails downstream, the approval is not lost.
Part of AI content pipelines02 · Locking and re-checking
Two runs, one poster
Two workflows can ask for the same event poster at the same moment. Without a lock, both would pay for an AI image and both would save a file. A bad generation must not leave a broken poster behind either.
Acquire Lock
Every contender writes a lock row to an n8n data table, then reads all the lock rows back.
Check Lock
The lowest live row id wins. Locks older than 30 minutes are ignored, so a crashed run can't block the event for ever.
Re-check Poster
After winning, look in Drive again. Another run may have saved the poster between the first look and taking the lock.
Validate Poster
Rejects anything that isn't an image or is under 20 KB before a single byte reaches Drive.
Capture Failure
Drive and Gemini errors leave through their error outputs and come here, so the lock is released on failure too.
Check Lock (generated from a tested logic.js, never edited in the canvas)
function wonLock(lockRows, myId, nowMs, ttlMinutes) {
const ttl = ttlMinutes * 60000;
const live = lockRows.filter(r => nowMs - Date.parse(r.createdAt) < ttl);
if (!live.some(r => r.id === myId)) return false;
return Math.min(...live.map(r => r.id)) === myId;
}One poster per event date, however many runs ask for it. The Code nodes are built from one JavaScript file with tests, so the logic can change without anyone editing the canvas.
Part of Roll The Dice Café03 · Failure policy
Alert on what matters, once
Fifty workflows can fail in fifty ways. Emailing on every failure trains you to ignore the emails. Staying silent hides real problems. Something has to decide which failures need a person.
On Workflow Error
One error handler for every live workflow, so the rules live in one place.
Failure History
Asks the studio log how many times this workflow has failed in the last 24 hours.
Classify
Sorts the error. Timeouts, rate limits and 5xx responses stay quiet until the third in 24 hours. Broken credentials raise one alert per credential, not one per workflow. Anything else escalates straight away.
Production Ledger Fail
If the failed run owns a production item, that item's retry budget applies. Retries are silent; escalation comes only when they run out.
Notify Dan
Studio: Notify is the only route to email: the same problem at most once every six hours. The email says what was tried, whether it's rescheduled and exactly what to do.
Classify (excerpt)
const TRANSIENT = /\b(429|500|502|503|504)\b|ETIMEDOUT|ECONNRESET|timed out|rate limit|quota/i;
const AUTH = /OAuth client was deleted|needs to be reconnected|invalid_grant/i;
const auth = AUTH.test(message);
const transient = !auth && TRANSIENT.test(message);The credential rule was added on 2 October, after one deleted OAuth client failed several workflows at once. The fix was one shared alert that names the credential to reconnect.
Part of Pi Ops04 · Confirm before marking done
Only a post ID counts as posted
A publishing step that marks a row as done before the platform confirms will lose posts without telling anyone. A retry that doesn't check will post the same video twice.
Compute Day Mod 3
Two channels share one TikTok account, so posting days follow a fixed rota by day number. No state to drift out of sync.
Random Delay (0-60 min)
A random wait of up to an hour, so posts don't land at exactly the same minute every day.
Get Next Unposted Short
Takes the oldest unposted short from an n8n data table.
Posted Successfully?
Success means Buffer's GraphQL response contains a post ID. A 200 response carrying an error doesn't count.
Mark Row Posted
The row is only marked once that ID exists. Otherwise it stays unposted, so tomorrow's run retries it automatically.
Buffer Post Failed - Raise
A failure throws on purpose, so it reaches the error handler instead of being recorded as a green run.
No silent losses and no double posts. This pattern came from finding a pipeline that had reported success for weeks while publishing nothing.
Part of AI content pipelines05 · Rules over guesswork
An inbox that sorts itself
Automated pipelines send a lot of email: approvals, alerts, summaries. The approvals must not get buried under newsletters.
Get Unlabeled Inbox Mail
Searches “in:inbox has:nouserlabels”, so anything I've filed by hand is never touched again.
Classify Email
Ordered rules on sender and subject, first match wins. Pipeline mail such as script approvals gets its own label, so it's never buried.
Mark As Read
Only newsletters and marketing are marked read, and only if they're still unread.
Plain rules in a Code node, because they're predictable, testable and free to run every hour.
Part of ATechStudio