🔒 Static Demo — no live outreach, credentials or external API calls All workflow logic is shown for architecture purposes only · no real n8n instance connected

Leadgen n8n System

B2B lead generation automation built on 11 interconnected n8n workflows. Covers the full chain: intent signal → profile enrichment → multi-LLM qualification → human-in-the-loop approval → personalised outreach → smart nurture with warmth decay.

11 n8n Workflows n8n · PostgreSQL · Docker Multi-LLM Consensus Warmth Decay Human-in-the-Loop 43 Tests
System Pipeline
Four groups of workflows form a complete B2B outreach system. Each workflow is independently deployable and replaceable.
Intake
01
Intent Radar
⏰ Every 15 min
→
02
Lead DNA Enrichment
🔗 Webhook
→
↓ enriched lead forwarded to Decision group
Decide
03
Multi-LLM Consensus
🤖 3 models vote
→
04
Human in the Loop
🔗 Webhook → Slack
→
↓ approved lead moves to Engage
Engage
05
Premium Outreach
▶ Manual / Reply
→
06
Smart Nurture
⏰ Daily cadence
+
07
Competitive Intel
⏰ Periodic scan
Ops
08
Command Center
⏰ Weekly report
09
Shadow Validation
▶ A/B testing
10
Global Error Handler
⚠ Error trigger
11
Live Dashboard
📊 Aggregation
Lead State Machine
How a lead's status changes as it moves through the pipeline
📡
Detected
intent_score > 0
→
🧬
Enriched
Lead DNA ready
→
🗳️
Scored
consensus ≥ 0.6
→
✅
Approved
human OK
→
📨
Contacted
outreach sent
→
🌡️
Nurturing
warmth decay
→
🏁
Converted / Archived
terminal state
11 Workflows
Each workflow is an independent JSON template. Import in order 01 → 11 into n8n, then configure credentials via UI.
WF-01
Intent Radar
Trigger
⏰ Schedule — every 15 min
Purpose
Monitor Reddit + Twitter/X for buying signals; deduplicate; classify intent with OpenAI; enqueue qualified leads
Output
New lead saved to Airtable; event forwarded to WF-02 via webhook
Nodes
15 nodes
HTTP Reddit HTTP Twitter/X AI Classifier Airtable dedup Idempotency gate
WF-02
Lead DNA — Deep Enrichment
Trigger
🔗 Webhook from WF-01 or external CRM
Purpose
Build a full Lead DNA profile: company (size, stack, funding), persona (role, interests) via Reddit, Apollo, Google, Claude
Output
Enriched Lead DNA card saved to Airtable; forwarded to WF-03
Nodes
11 nodes
Apollo.io Reddit profile Google fallback Claude profiler Score calc
WF-03
Multi-LLM Consensus
Trigger
▶ Manual (full consensus) / quick draft for warm leads
Purpose
GPT-4o writes strategy, Claude crafts message, Quality Gate validates; weighted vote → consensus score; escalates to Human if divergence > 30%
Output
Approved draft + consensus_score saved to queue in Airtable
Nodes
9 nodes
GPT-4o strategy Claude craft Quality gate Merge & vote Airtable queue
WF-04
Human-in-the-Loop
Trigger
🔗 Webhook when message ready for review
Purpose
Format Slack card with full Lead DNA, draft message, consensus score; sales rep approves / edits / rejects via Slack button
Output
Approved message forwarded to WF-05; rejection re-enters WF-03
Nodes
6 nodes
Slack card Review log Airtable update
WF-05
Premium Outreach + Conversion Funnel
Trigger
▶ Manual outreach / Inbound reply webhook
Purpose
First personalised touch via LinkedIn/Reddit (manual Slack prompt) or email. Reply analysis: route to calendar / blacklist / FAQ / nurture
Output
Contact logged to Airtable; follow-up scheduled in WF-06
Nodes
18 nodes
Slack LinkedIn prompt Reply analyzer Calendly route Blacklist Follow-up scheduler
WF-06
Smart Nurture + Warmth Decay
Trigger
⏰ Schedule — daily
Purpose
Fetches all active leads; applies warmth decay formula (×0.9 per inactive day); AI decides next action per warmth bucket; archives cold leads
Output
Updated warmth scores in Airtable; follow-ups queued or leads archived
Nodes
11 nodes
Warmth decay fn AI next action Channel change Archive cold
WF-07
Competitive Intelligence
Trigger
⏰ Periodic competitor scan
Purpose
Searches Reddit for competitor complaints; OpenAI scores opportunity; enriches leads whose competitors have issues with personalised angles
Output
Comp-intel score saved; messaging angle updated in Lead DNA
Nodes
7 nodes
Reddit complaints Sentiment score Opportunity angle
WF-08
Command Center
Trigger
⏰ Weekly report schedule
Purpose
Aggregates pipeline metrics; AI generates insights + recommendations; posts weekly Slack digest; acts as orchestration hub for manual re-triggers
Output
Weekly Slack report; pipeline snapshot saved to Airtable
Nodes
5 nodes
Aggregate metrics AI insights Slack digest
WF-09
Shadow Mode Validation
Trigger
▶ Manual — shadow run parallel to production
Purpose
New workflow version processes same leads without real outreach; AI compares shadow vs production draft quality; score delta decides promotion
Output
Shadow score logged; Slack alert if delta significant
Nodes
6 nodes
Shadow check AI compare Delta score
WF-10
Global Error Handler
Trigger
⚠ Error trigger — catches failures from any workflow
Purpose
Classifies severity (INFO / WARNING / CRITICAL); logs to Airtable ErrorLog; CRITICAL → immediate Slack alert; WARNING → Slack digest; retry via Command Center
Output
All errors centralised; CRITICAL errors page on-call
Nodes
8 nodes
Severity classify ErrorLog CRITICAL alert WARNING digest
WF-11
Live Results Dashboard
Trigger
▶ Manual aggregation trigger
Purpose
Reads live data from Airtable (leads, outreach, warmth, errors); prepares structured payload for dashboard; surfaces real-time funnel metrics
Output
Dashboard data payload; funnel state visible in Airtable views
Nodes
3 nodes
Airtable read Aggregation Dashboard payload
Mock E2E Trace
Based on tests/mock_lead_payload.json. Shows the full pipeline run for a single lead — synthetic data, no real outreach executed.

📋 Lead: Alex Johnson @ Acme Technologies

lead_idtest-lead-001
companyAcme Technologies (SaaS, 50–200)
titleHead of Sales
locationRemote / New York
signaljob_posting — "Hiring 3 Senior AEs"
sourceintent_radar
intent_score0.78
warmth60 (initial)
idempotency_keye308cb1b5e34d52b
📡
WF-01 · Intent Radar PASS
Reddit search: "hiring SaaS sales team" → matched Acme Technologies job post.
Idempotency check: key e308cb1b5e34d52b → not seen before → proceed.
AI intent classifier → intent_score = 0.78 ≥ threshold 0.6 → qualified.
Lead saved to Airtable. Webhook fired to WF-02.
signal_typejob_posting
intent_score0.78
idempotencyNEW — enqueued
🧬
WF-02 · Lead DNA Deep Enrichment PASS
Apollo.io → company size 50–200, funding $12M Series A, stack: Salesforce + HubSpot.
Reddit profile → Alex posted in r/sales 3× this month, focused on "AE hiring frameworks".
Claude profiler → pain points: ramp time for AEs, forecasting accuracy.
Score: company_fit=0.85 + persona_fit=0.80 + signal_strength=0.78 → enrichment_score = 0.81.
enrichment_score0.81
pain_pointsramp time, forecasting
stackSalesforce + HubSpot
🗳️
WF-03 · Multi-LLM Consensus AGREEMENT
Three models scored the lead independently, then a weighted vote was computed:

Model Role Score Weight
GPT-4oStrategy0.821.0
ClaudeMessage craft0.750.9
GPT-4o miniQuality gate0.791.1

compute_consensus_score([0.82, 0.75, 0.79], weights=[1.0, 0.9, 1.1])
consensus_score0.788
divergence0.07 ≤ 0.3 → agreement=True
actionforward to Human Review
✅
WF-04 · Human-in-the-Loop REVIEW
Slack card sent to #lead-review with full Lead DNA, draft message, consensus score 0.788.
Sales rep reviewed in Slack → clicked Approve.
Interaction logged to Airtable. Approved draft forwarded to WF-05.
reviewer_actionapproved
time_to_review~4 min
📨
WF-05 · Premium Outreach SCHEDULED
Personalised LinkedIn message prepared using Lead DNA (pain: ramp time, stack: Salesforce).
Slack prompt sent to sales rep: "Ready to send to Alex Johnson — click to confirm LinkedIn DM".
[Demo: no message actually sent] — in production the rep clicks Confirm in Slack.
Contact event logged to Airtable. Follow-up scheduled in WF-06 (+3 days).
channelLinkedIn DM
personalisationramp time + Salesforce angle
follow_up_in+3 days
🌡️
WF-06 · Smart Nurture · Daily Run (Day +1) PASS
Daily schedule triggers. Fetches Alex Johnson: warmth=60, days_inactive=1, has_recent_reply=False.
apply_warmth_decay(60, days_inactive=1, decay_rate=0.9) = round(60 × 0.9¹) = 54.
warmth_to_bucket(54) → "warm" (threshold: ≥40 and <70).
AI next action for "warm" bucket: send light-touch follow-up in 2 days via LinkedIn.
warmth_before60
warmth_after54
bucketwarm
next_actionfollow-up in 2 days
📊
WF-11 · Live Dashboard (background) PASSIVE
Airtable views updated in real-time throughout the run.
WF-11 next manual trigger will aggregate: 1 lead contacted, warmth=54 (warm), 0 errors, consensus=0.788.
Engineering Logic
Key patterns from workflow_utils.py — Python mirrors of n8n Code nodes, fully unit tested (43 tests).
🔑
Idempotency Gate (WF-01)
Every incoming event gets a deterministic SHA-256 key computed from its identity fields. Duplicates (same lead seen again within the scan window) are silently dropped before any enrichment or outreach runs.
def make_event_key(event_data, key_fields=None): fields = key_fields or ["id", "source", "url", "company", "email"] parts = [str(event_data.get(f, "")) for f in fields] raw = "|".join(parts).encode("utf-8") return hashlib.sha256(raw).hexdigest()[:16] # Mock payload result: make_event_key({"source": "intent_radar", "company": "Acme Technologies", "email": "alex@acme.example.com"}) # → "e308cb1b5e34d52b"
🌡️
Warmth Decay Formula (WF-06)
Lead "temperature" decays 10% per inactive day. A recent reply boosts warmth by +30 (capped at 100). Warmth bucket drives nurture cadence: hot = immediate, cold = re-activation sequence.
def apply_warmth_decay(warmth, days_inactive, has_recent_reply=False): decayed = warmth * math.pow(0.9, max(0.0, days_inactive)) result = round(decayed) if has_recent_reply: result = min(100, result + 30) return result # Buckets: hot ≥70 · warm ≥40 · cool ≥15 · cold <15 apply_warmth_decay(60, days_inactive=1) # → 54 (warm) apply_warmth_decay(60, days_inactive=7) # → 32 (cool) apply_warmth_decay(60, days_inactive=1, has_recent_reply=True) # → 84 (hot)
🗳️
Multi-LLM Consensus (WF-03)
Three models score the lead independently. Weighted average gives the consensus score. If max−min > 0.3 (30% divergence), the lead is escalated to Human Review rather than auto-approved.
def compute_consensus_score(scores, weights=None): w = weights or [1.0] * len(scores) consensus = sum(s*wt for s,wt in zip(scores,w)) / sum(w) divergence = max(scores) - min(scores) return {"score": round(consensus,3), "agreement": divergence <= 0.3, "divergence": round(divergence,3)} compute_consensus_score([0.82, 0.75, 0.79], weights=[1.0, 0.9, 1.1]) # → {score: 0.788, agreement: True, divergence: 0.07}
⚠️
Error Classification + Retry Policy (WF-10)
All workflow failures route to the Global Error Handler. Severity is classified by first regex match. Critical errors page immediately; warnings batch to Slack digest; info is logged only.
def classify_error_severity(error_message): if re.search(r"timeout|ECONNREFUSED|401|403|5\d\d|fatal", msg): return "CRITICAL" if re.search(r"rate|retry|warn|429", msg): return "WARNING" return "INFO" # Retry policy: # external_api → 3 attempts, backoff [5s, 15s, 45s] # llm_api → 2 attempts, backoff [10s, 30s] → human review # webhook → 5 attempts, backoff [2s…60s] → log & skip
👤
Human-in-the-Loop Design (WF-04)
Two stop-points keep humans in control: (1) before first outreach for all high-value leads, (2) when multi-LLM divergence exceeds 30%. Slack interactive buttons allow approve / edit / reject without leaving the chat.
# Escalation conditions (in n8n router node): if consensus["agreement"] == False: # divergence > 0.3 → Human review (mandatory) if lead["enrichment_score"] >= 0.75: # high-value → Human review (standard) # Slack card includes: # - Lead DNA summary (company, persona, pain points) # - Draft message from Multi-LLM # - Consensus score + divergence # - Buttons: [Approve] [Edit] [Reject]
🔬
Shadow Mode Validation (WF-09)
Before promoting a new workflow version to production, it runs in shadow mode on real leads. Results are compared against production output without affecting real outreach. Delta score decides promotion.
# Shadow run flow: 1. New WF version runs in parallel → shadow draft 2. AI compares shadow vs production draft quality 3. compute delta_score = shadow_score - prod_score 4. if delta_score > 0.05: Slack alert → manual promotion decision elif delta_score < -0.05: Slack alert → block promotion else: Log "no significant diff" → auto-promote option # Real outreach: never triggered in shadow mode