On this page
Seventy nine lead drafts sit in one live table on census day, and zero had been sent. That table belongs to our own HQ drafter, a TypeScript tool that has been live since 24 September — it is not this workflow's output. Episode 09 rebuilds that same drafting logic as an n8n workflow, and every on-screen run in the video comes from the rebuild, not from the 79-row history.
A lead row goes in, a personalised draft comes out, and nothing sends itself. Leads are deduped by host — a chain touched twice by the same company is one lead, not two drafts — then one model node writes a short outreach draft from each lead's own public signals. Every surviving draft lands in Postgres with a stage of draft-ready or needs-review. There is no send node anywhere on the canvas.
If iframes are blocked where you are reading this, the video is at the Episode 09 video.
.example host, never a real third party.
What does the draft personaliser actually do?
A batch of leads is loaded, masked to a stable host reference before anything reaches storage, then deduped by host so a lead harvested twice through different branches only produces one draft. A cap guard reads the daily model-call budget before the model is ever called and slices the batch to whatever headroom remains, throwing rather than silently truncating to zero. One model node, over OpenRouter, writes a personalised draft from each surviving lead's own public signals. A schema gate then rejects any draft over 120 words or carrying a link that was not already in the source row, and marks everything that survives with a stage of draft-ready or needs-review before it reaches Postgres.
What did the rebuild's own test run actually show?
Seven test leads went in — five distinct hosts, two duplicates by host built in on purpose to prove the dedupe step, and one edge row with no usable public signal. Five distinct hosts survived the dedupe step, five model calls were made, and five rows were written: four marked draft-ready, one marked needs-review — the edge row with no signal, correctly scored low-confidence.
| Metric | Observed |
|---|---|
| Leads in the test batch | 7 |
| Duplicates dropped by host | 2 |
| Drafts written | 5 |
| Marked draft-ready | 4 |
| Marked needs-review | 1 |
| Rejected by the schema gate | 0 on this run |
| Wall clock for 5 drafts | 56.5 seconds |
| Drafts sent | 0 — there is no send node |
A second, older number sits next to the 79 honestly: 34 direct messages went out in September across Instagram and LinkedIn, and 0 recorded replies came back — recorded, because there is no reply column yet on that table, not because a reply is known to be impossible.
The cap sheet
Gotchas worth knowing before you fork this
A rejected draft is tested directly, not just asserted: a 130-word draft fed into the schema gate's own stored code threw on the word count, and a draft naming a host that was not the lead's own host threw on the link check. Both were confirmed live, unprompted, on a real run — a model wrote a plausible but foreign-looking domain for one lead, and the gate rejected it.
Masking happens in the earliest node to touch a lead row, before dedupe, before the model, before anything is written — never rely on a later node to mask something that has already reached storage. And the model's own output does not carry the input item's other fields through automatically; the host reference has to be pulled by node reference from the upstream item, or every draft's key comes back malformed.
How do I import it and where are the swap points?
The free workflow JSON is MIT-licensed at github.com/waseemnasir2k26/n8n-workflows/tree/main/workflows/09-lead-draft-personaliser. Create the caps Data Table, run the schema against your own Postgres, import the workflow and its error workflow, set your own OpenRouter credential, and load your own lead batch before running the manual test path.
Swap points built into the canvas on purpose:
- A webhook lead-intake node, shipped disabled — a future inbound lane (a form, a harvest hook) would post leads here instead of a sample file.
- A GitHub sample-lead loader, shipped disabled until your own fork of this repo is public — a Postgres loader carries the same sample rows until then.
- A daily model-call budget on the caps row, enforced before the model is ever called, so a runaway loop cannot silently spend past what you set.
Frequently asked questions
Did this n8n workflow produce the 79 drafts?
No. The 79 drafts sit in a table owned by our HQ TypeScript drafter, live since 24 September. This episode rebuilds that drafting logic as an n8n workflow; the on-screen figures — five leads in, five drafts out — come from the rebuild's own test run, not from the 79-row history.
Does this workflow send anything?
No. There is no send node anywhere on the canvas. Every surviving draft lands in Postgres marked draft-ready or needs-review, and a person decides what, if anything, leaves.
How does it avoid drafting the same lead twice?
A dedupe step keeps the earliest occurrence per host within a batch and drops the rest — a lead reached through two branches of the same company is one lead, not two drafts.
What happens when a draft fails quality checks?
A schema gate rejects anything over 120 words or carrying a link that was not already present in the lead's own row, and the rejection is counted in the run summary rather than silently dropped.
The rest of the series
One workflow per video, the JSON given away under an MIT license each time, built by Waseem Nasir at SkynetLabs.
- Episode 05, n8n clinic WhatsApp booking agent: a code guard hands off medical and price questions before the model.
- Episode 06, n8n Meta ads circuit breaker: our own breaker missed our own overspend, and this one closed the gap.
- Episode 08, n8n silent lane watchdog: one lane missed a test schedule, one was just quiet.
Want a lead drafter tuned to your own outreach, with the send decision kept with a person? Book a discovery call with SkynetLabs.
WhatsApp +92 300 1001957 · Waseem Nasir, SkynetLabs
Hire SkynetLabs, our Top Rated agency on Fiverr: https://www.fiverr.com/agencies/skynetjoellc