Most lead generation automation is sold on what it does when everything works.
The demo runs. Leads appear in a spreadsheet. The nodes light up green, left to right.
I'm Ussama Assad. I build these systems, and I spend more time diagnosing broken ones than building new ones. Here is what I have learned watching them fail: almost every workflow has guards, and almost nobody has specified what the guard does when the guard itself breaks.
That sounds like a small omission. It is the difference between a bad day and a bad month.
The question nobody asks in the demo
Your workflow has a check in it somewhere. A filter that decides which companies are worth enriching. A validator that decides which emails are safe to send to. A budget check that decides whether today's run can afford to proceed.
Ask the person who built it one question:
"What happens to the run when that check throws an error?"
There are only two possible answers, and most systems have never chosen between them deliberately.
- Fail open. The check is unavailable, so the workflow carries on without it.
- Fail closed. The check is unavailable, so the workflow refuses to proceed past it.
Fail open is almost always the accidental default, because it is what you get by writing no error handling at all. And it has a specific, expensive property: the failure mode of your safety check is the dangerous thing happening at full speed, with nothing in the way.
A filter that normally rejects nine out of ten candidates, when it breaks, does not reject zero out of ten quietly. It passes all ten to the paid step behind it. You do not get a smaller result. You get a bigger bill.
Three shapes this takes in a lead generation workflow
1. Ungoverned consumption
Every paid lookup — email finding, enrichment, verification — draws on a finite monthly allowance. If nothing tracks consumption against that allowance as the run proceeds, you find out you have run out when the pipeline output drops and somebody asks why.
The failure is not the overspend. It is that the step went dead and nothing said so loudly. The workflow kept executing. The nodes still went green. There was simply nothing in the output, and green does not distinguish "found nothing" from "could not look."
This one is mine, too. I built a spend cap for my own measurement runs and it had a blind spot: spend was recorded only when a run wrote a result. A run killed halfway through wrote nothing, so it cost real money and the cap never saw it. A cap with a blind spot is not a cap — it is a number that happens to be right most of the time.
2. Paying before deciding
This is an ordering problem and it is very easy to build by accident.
The natural way to assemble a workflow is in the order the data becomes available. You have a company, so you enrich it. You have a contact, so you verify them. You have a verified contact, so you draft a message. Only at that last step does anything judge whether this was a company worth contacting at all.
Every rejection at that final step is correct. Every one of them arrives after the money is gone.
The judgment that rejects most of the work should be the cheapest step, and it should run first. A small model reading the original signal — the job post, the listing, whatever triggered the row — can usually decide "not a fit" for a fraction of a penny, before a single paid lookup fires.
I got the same ordering wrong in a different register. I projected a run's cost from one narrow sample, launched on that projection, and killed it three calls in when the real rate turned out to be several times higher. I had written the rule about not extrapolating from unrepresentative samples. I extrapolated anyway, in the same week.
3. The guard that fails open
Build the cheap filter from the last section and you have solved the ordering problem — right up until the filter times out.
If the fallback is "enrich everything anyway," you have built something worse than no filter. A missing filter is a gap you can see and budget around. A filter that silently stops filtering, while the workflow reports success, spends your allowance at the exact rate you built it to prevent.
And it can do this without ever looking broken. A guard wrapped in its own fail-safe can throw on every single run, get caught, get swallowed, and report clean — saving nothing, for days, while the dashboard stays green.
That specific shape has bitten me more than once. My own guard skipped its check entirely whenever a required field was simply absent from the record — the field was missing, so the condition was never true, so the check quietly passed. Absence was read as permission. If a guard cannot confirm the thing it exists to confirm, it has to refuse, not shrug.
The five mechanisms worth paying for
If you are hiring someone to build this, these are reasonable things to expect. None of them is exotic. All of them are boring infrastructure that only proves its worth on the bad day.
1. Decide before you pay. The cheapest judgment runs first, on the free signal you already have. Most candidates should be eliminated before any paid step touches them.
2. Fail closed on spend. When a gate errors or times out, the workflow does not proceed to the expensive step. At most it processes a small, capped, top-ranked subset and parks the rest visibly for a human. There should be no code path where a broken guard causes unbounded spend.
3. A ledger, not a vibe. One place that records what was consumed, checked before the run starts rather than after it ends. A preflight that refuses to start the morning run when the remaining allowance cannot cover it. A consumption line in whatever report you actually read.
4. Memory, so you never buy the same answer twice. Two different caches, doing two different jobs. One remembers results, so an empty answer is not re-purchased every time the row reappears. One remembers that you already paid for this company, with a window longer than your input's natural repeat cycle — if you re-scrape a 30-day window daily, the same company will resurface for 30 days, and without that ledger you will pay for it 30 times.
5. Extract more per unit of spend. One domain lookup often returns a whole team. Rank them and keep the several who are plausible buyers rather than discarding everyone but your first guess. Same spend, several times the usable output.
The part that matters most: something that checks the checker
Here is the uncomfortable thing about automation with AI steps in it. The system will sometimes report that it did something it did not do. Not maliciously — a step returns a plausible summary, a log line records an intention rather than an outcome, an error gets caught and described rather than raised.
So the operating principle worth adopting is blunt:
Claims are not records.
No number is trusted because a log line says it. Every claim the run makes about itself gets re-derived from primary sources — the actual output files, the platform's own API, the ledger — by something that did not produce them.
Three properties make that worth having, and the second and third are the ones people skip:
It runs independently. On its own schedule, separate from the workflow it judges. A verification step inside the pipeline shares the pipeline's failure modes — if the run dies early, the check dies with it and reports nothing, which reads identically to reporting fine.
It is itself verified. Something has to notice when the verifier did not run. Otherwise a dead verifier is indistinguishable from a clean bill of health, and silence gets read as good news.
It is lie-tested. This is the one almost nobody does. Feed it a fabricated day — a log claiming five leads over an empty output — and confirm it convicts. Then feed it an honest zero-day and confirm it stays quiet. A checker that has never once failed should be assumed incapable of failing until proven otherwise.
I will be straight about where I am on that last one: I have a check that guards my own records, I have reasoned carefully about what it covers, and I have never fed it a deliberate lie to confirm it catches one. It is on my list. Until it is done, I hold its green result more loosely than I would like to.
What to ask before you hire
Five questions. The answers tell you more than any demo.
- "What does this workflow do when the filter times out?" If the answer is "it keeps going," the guard is decoration.
- "Where does it record what it spent, and what reads that before the next run?" If spending is only visible after the fact, it is not governed.
- "What stops it buying the same record twice?" Ask specifically how long that memory lasts, and compare it to how often the same input reappears.
- "What re-checks what the run says it did?" And then: what notices if that does not run?
- "Has the checker ever been tested against a false record?" An honest "no, not yet" is a fine answer. "It's never failed" is not.
I'm Ussama Assad, and this is the bar I hold my own lead generation builds to. None of this makes a lead generation system produce more meetings. That depends on your offer, your market and your list, and anyone who tells you otherwise is selling you something they cannot control.
What it does is make the system survive its own bad days without quietly costing you money — and make it possible to find out, within hours rather than weeks, when it is wrong about itself.
That is the part you are actually buying.
Written by Ussama Assad. More on how I build and diagnose automated lead generation systems: https://ussama.dev
Top comments (0)