Automation · 10 steps
Cold email list personalization: clean names and a one-line opener on every row
A messy scraped lead list goes in. A send-ready list comes out, with a natural first name, a company name a person would say and a one-line opener on every row.
Built by Matt Cruz, Modern Code Consulting
The problem
A scraped list reads like a scraped list. "Hi Nicholas" at "HARLOWE PROPERTY GROUP, LLC" tells the reader nobody looked at the email before it went out.
Fixing names by hand is fine for 20 rows. It does not hold up for a list you send every week, and a single bad row (a surname in the first name field, a blank state) is the one that gets screenshotted.
What I built
I built this as a Claude Code skill. The list gets split into small chunks and subagents clean the names in parallel, returning the nickname and the company name a person would actually say.
A Python script does everything exact: merging the names back onto the rows, rendering the opener template and naming the file for staging. Then the skill sweeps for rows that should not send and holds them out in their own file, each with a reason.
How it works, step by step
A Claude Code skill runs the job. Subagents do the judgement on names; one Python script does everything exact.
Stage 1 · Lead list in
Read the scraped lead list
RanLoads the list with the script's own reader and checks the columns the skill needs: first name, company, city and state.
Lead CSV
Split the list into chunks
RanWrites one small text file per chunk with just the row number, first name and company name, so each agent reads only what it needs.
personalize_leads.py
Stage 2 · Clean up the names
Clean names 1
RanOne subagent reads 20 lines and returns a nickname and a conversational company name for each, as JSON.
Subagent, personalize-leads skill
Clean names 2
RanOne subagent reads 20 lines and returns a nickname and a conversational company name for each, as JSON.
Subagent, personalize-leads skill
Stage 3 · Write the opener
Merge names and render openers
RanJoins both agents' JSON back onto the rows, undoes any HTML escaping, adds the lowercase state and renders the opener template on every row.
personalize_leads.py
Stage 4 · Enrich and check
Scan homepages for software
Not runWould fetch each company's homepage and look for 14 software signatures, adding columns that sort the list by likely spend.
personalize_leads.py, company websites
Flag rows that should not send
RanRuns the skill's mandatory sweep for surnames, cities and blank states, spot-reads the openers, then judges each flagged row: four held out, one kept.
personalize-leads skill
Report counts and open flags
RanWrites the hand-off note: row counts, the by-state split, what was held and why, and the one-variable rule for the email body.
personalize-leads skill
Stage 5 · Stage for sending
Name the file for staging
RanSaves the 36 send-ready rows under the date-source-segment file name the staging convention asks for, with the held rows in their own file.
Send-ready CSV
Stage the list in a Google Sheet
Not runWould export the named CSV to a new Google Sheet for a last look before it is loaded into the sending tool.
Google Sheets
The recorded run
This is a real run of the automation on an invented business: a maker of HVAC maintenance software. Use the tabs inside it to move between the overview, how it's built, the step-by-step run and the result.
What ran for real: Invented sender, leads and domains. The prep and merge functions are the real script's, run against a scratch copy of the folder layout (shown here with the pipeline's usual relative paths), and the name clean-up was done by two real subagents in parallel. The homepage scan and the Google Sheet staging were not run.
The run is laid out for a desktop screen.
More automations
- Verified lead list, catch-alls held backA saved people search goes in and a deduplicated, verified, import-ready lead file comes out, with every catch-all address held back.
- Cold email reply reviewEvery campaign's numbers and every reply go in, and the positive reply rate, what changed since last time and the lines of copy behind the replies come out.
- Paid deposit to project kickoffA client pays the deposit, the deal closes, a dated project plan is built and a kickoff document is ready, with no hand on the CRM.
- Keeping a long multi-agent build on trackA project board goes in, and any fresh session can pick the build up from one file and one board read.
Want something like this running in your business?
Book a free 15-minute call. Tell me where the hours and the leads are leaking, and I'll tell you what I'd automate first and what it would cost.
Book a free automation audit