Home / Work / 03

Case study 03 · Pipeline and evaluation

Outreach pipeline with a linter on AI-written email

I ran cold outreach for my own small web studio. The sending was easy. The AI-written parts were where it broke.

When
June to September 2026
Stack
Python, Playwright, Apify, LLM drafts
Scale
1,604 companies emailed
Result
1 paying client

01 · Result first

1,604
companies emailed
1
paying client · www.galerie-krasy.cz

That is a bad number and I am putting it first on purpose. The useful part is what it taught me about AI-written personalisation.

02 · Pipeline

Scrape, measure, draft, lint, approve

Find

Leads from public business registers and maps with Playwright and Apify. A ledger keyed by company ID, then domain, then email, so nobody gets contacted twice.

Measure

One real fact per company, measured on its live website that day. On JavaScript sites the source of truth is the rendered page, not a fetched HTML string.

Draft and check

An LLM writes the draft. A linter checks it. A human approves every email. Nothing is sent automatically.

03 · The linter

mail_check.py, built from every rejected draft

Every draft that got rejected got a short written retro. Mistakes that repeated became rules: invented assumptions about the lead, stock phrases, the wrong grammatical gender for the recipient, the same opening line twice in a thread, more than one question at the end. A failed check returns exit code 1 and blocks the draft.

Its limit, learned the hard way: a linter checks form, not truth. A perfectly clean email can still say something false about the company. That is why the fact has to be measured, not generated.

04 · Judgement

Where I said no

  • Turned down a paid scraping job that meant harvesting private phone numbers for unsolicited SMS and getting around the site's protections. I wrote down why.
  • Looked up the Czech legal frame for cold email and moved towards replying to published requests, which is not unsolicited.
  • Caught my own records overstating income: an unpaid quote had been logged as revenue. Corrected it in 15 files.