n8n orchestrates, LangGraph writes, a human approves: an AI cold-email pipeline that can't spam
"AI SDR" demos usually end with an agent blasting a few hundred emails. In real B2B sales that burns your domain in a week, and the emails are full of claims nobody can back up. I built a lead-generation pipeline for an
"AI SDR" demos usually end with an agent blasting a few hundred emails. In real B2B sales that burns your domain in a week, and the emails are full of claims nobody can back up.
I built a lead-generation pipeline for an export manufacturer with three hard rules:
- Every claim in an email must come from sourced data, quoted from what the APIs returned.
- A rule checker rejects bad drafts and sends them back for a rewrite (max 2 times).
- Nothing is sent until a human clicks Approve.
Here is how the work is split, which I think is the useful part.
n8n does the plumbing, LangGraph does the thinking
n8n (55 nodes) handles everything deterministic:
- Triggers (manual, schedule, webhook)
- Apollo for finding people and enriching emails, Hunter for verifying them
- Dedupe against what is already in the CRM, qualification filters
- HubSpot (or a Google Sheet) as the CRM
- An approval link per draft, an unsubscribe link, and a suppression list checked again right before sending
- SendGrid (or Gmail / SMTP) to send, behind a
SEND_ENABLEDflag
A LangGraph service (FastAPI) does the parts that need a model:
research -> write -> check -> (rewrite, max 2) -> draft for approval
- research writes a short note about the company, and every reason must carry a verbatim quote from the lead data, or it is cleared.
- write drafts the email from that note only.
- check is plain code, not another LLM: it blocks numbers that are not in the research or approved facts, unsupported claim phrases, placeholders and empty fields, a missing unsubscribe line, and over-long drafts.
- Failing drafts go back to a rewrite step with the checker's reasons. After two failed rewrites the draft is flagged
needs_human_fix, never sent.
The model is any OpenAI-compatible endpoint (OpenAI, DeepSeek, Qwen, OpenRouter), so you are not locked in.
Why the checker is code, not an LLM
An LLM grading an LLM tends to agree with itself. A regex and a list of allowed numbers does not. The checker is boring on purpose, and it is the reason I trust the drafts enough to show them to a salesperson.
Industry is a JSON file
Target industry, buyer type, product lines and selling points live in one profile file, and a small script turns it into the n8n variables. The kit ships with a battery-storage example and an LED-lighting example to show how to swap.
How I tested it
- 21 pytest tests for the LangGraph service, 8 JS tests for the n8n Code nodes
- A live run against a real n8n instance (1.123.87) with local mock Apollo / Hunter / HubSpot / SendGrid: 26/26 checks pass
- 10 fake leads end to end: 6 drafts waiting for approval, 2 disqualified, 1 deduped, 1 blocked by the unsubscribe list
To be clear: these runs used mocked vendor APIs and a deterministic fake model. You bring your own Apollo, Hunter, HubSpot, SendGrid and LLM keys.
Get the template
The full kit (importable n8n workflow, LangGraph + FastAPI service, Docker Compose, tests, sample profiles) is here:
👉 B2B AI Lead Generation: n8n + LangGraph template, $29
I also wrote about a WhatsApp bot where the LLM is only allowed to pick an FAQ id: WhatsApp AI Customer Service Bot kit, $19.
Questions about splitting work between n8n and LangGraph are welcome in the comments.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.