Skip to content

Instantly share code, notes, and snippets.

@financiallywelloff-cmyk
Created May 3, 2026 20:12
Show Gist options
  • Select an option

  • Save financiallywelloff-cmyk/db258fcc83e4a423466c72ce50e8c5f8 to your computer and use it in GitHub Desktop.

Select an option

Save financiallywelloff-cmyk/db258fcc83e4a423466c72ce50e8c5f8 to your computer and use it in GitHub Desktop.
n8n AI lead qualification workflow with human approval

n8n AI Lead Qualification With Human Approval

A practical human-in-the-loop n8n workflow pattern for AI lead qualification.

The point is not to make the workflow fully autonomous. The point is to let the model make one scoped judgment step, then require human approval before anything touches customers, outreach, money, publishing, or production.

Pattern

Trigger
-> sample lead intake
-> qualify lead
-> risk check
-> human approval for hot leads
-> nurture route for lower-risk leads

Why This Exists

Most broken AI agent workflows try to make every part of the system agentic.

That usually creates a workflow that is expensive, hard to debug, and scary to trust.

This pattern keeps the lanes clean:

  • n8n handles triggers, routing, retries, credentials, and run history.
  • The model handles one fuzzy decision.
  • A human approves high-consequence output.

Included Workflow

File:

n8n-ai-agent-human-approval.json

This is a starter workflow. It uses a manual trigger, sample lead data, a qualification decision step, a human approval gate for hot leads, and a nurture route for lower-risk leads.

Replace the scoring stub with your own AI Agent node, Claude/OpenAI call, or n8n AI chain plus Structured Output Parser.

Example Source Pattern

This public starter is based on a real lead qualification and routing pattern:

Webhook intake
-> enrich lead data
-> AI lead qualification
-> structured output parser
-> route into hot lead, nurture, alert, and email paths

The original workflow stays private. This template keeps the reusable shape without exposing credentials, customer data, or business-specific routing.

When To Use It

Use this for workflows like:

  • triaging leads before outreach
  • qualifying demo requests
  • routing support requests by urgency
  • classifying support requests
  • scoring Search Console opportunities
  • routing content ideas into an approval queue

Do not use it for simple if/then automation. If the rules are obvious, use normal workflow automation.

Ship Lean Rule

Claude/Codex builds.
n8n runs.
Human approves.

Related Resources

{
"name": "Ship Lean - AI Lead Qualification With Human Approval",
"nodes": [
{
"parameters": {},
"id": "a1b9d72f-2d86-4f8c-bb95-2fb687227d4a",
"name": "Manual test trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [200, 300]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "lead_name",
"name": "lead_name",
"type": "string",
"value": "Jordan Lee"
},
{
"id": "company",
"name": "company",
"type": "string",
"value": "Northstar Ops"
},
{
"id": "message",
"name": "message",
"type": "string",
"value": "We have three people manually moving inbound demo requests from forms into Airtable, Slack, and email. I want an AI workflow that qualifies leads and routes urgent ones without losing control."
},
{
"id": "source",
"name": "source",
"type": "string",
"value": "website_contact_form"
},
{
"id": "budget",
"name": "budget",
"type": "string",
"value": "$3k-$8k"
}
]
},
"options": {}
},
"id": "ea5f40bb-52d5-4205-83ab-76da35fd0f6f",
"name": "Sample lead intake",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [440, 300]
},
{
"parameters": {
"jsCode": "const lead = $input.first().json;\n\n// Teaching stub: replace this with an AI Agent node, Claude/OpenAI call,\n// or n8n's AI chain + Structured Output Parser.\n// Keep the output structured so normal n8n nodes can route it reliably.\nconst text = `${lead.message} ${lead.budget}`.toLowerCase();\nconst urgency = /urgent|asap|this week|losing|manual|broken/.test(text) ? 3 : 1;\nconst fit = /workflow|automation|airtable|slack|email|lead|demo|ops/.test(text) ? 3 : 1;\nconst budget = /\\$3k|\\$5k|\\$8k|budget/.test(text) ? 2 : 0;\nconst score = urgency + fit + budget;\nconst tier = score >= 7 ? 'hot' : score >= 4 ? 'nurture' : 'low_fit';\nconst approvalRequired = tier === 'hot';\n\nreturn [{\n json: {\n ...lead,\n qualification_score: score,\n tier,\n reason: tier === 'hot'\n ? 'Strong fit, urgent operational pain, and clear budget signal.'\n : tier === 'nurture'\n ? 'Some fit, but needs more qualification before outreach.'\n : 'Not enough fit or urgency for immediate follow-up.',\n suggested_next_step: approvalRequired\n ? 'Human reviews and approves same-day outreach.'\n : 'Add to nurture queue for slower follow-up.',\n needs_human_approval: approvalRequired\n }\n}];"
},
"id": "b96d2a6f-c92e-4c38-8ad0-b5d7997f3e63",
"name": "Qualify lead decision",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [680, 300]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "needs-approval",
"leftValue": "={{ $json.needs_human_approval }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "2a0e4529-7d4b-4f9a-8aef-c8b753b77142",
"name": "Needs human approval?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [920, 300]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "status",
"name": "status",
"type": "string",
"value": "waiting_for_human_approval"
},
{
"id": "approval_channel",
"name": "approval_channel",
"type": "string",
"value": "Slack, Notion, Linear, or manual n8n review"
},
{
"id": "safe_action",
"name": "safe_action",
"type": "string",
"value": "Review before sending outreach, CRM updates, or public/customer-facing messages."
}
]
},
"options": {}
},
"id": "c9972ab7-df84-4d7e-99be-f8e0cb2587cb",
"name": "Queue hot lead for approval",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [1180, 180]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "status",
"name": "status",
"type": "string",
"value": "added_to_nurture_queue"
},
{
"id": "route",
"name": "route",
"type": "string",
"value": "CRM, Airtable, Notion, or email nurture list"
},
{
"id": "safe_action",
"name": "safe_action",
"type": "string",
"value": "No immediate customer-facing action."
}
]
},
"options": {}
},
"id": "2b5fa6ed-26cc-4c15-9fd1-2ddc6fdf0767",
"name": "Route nurture lead",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [1180, 420]
}
],
"connections": {
"Manual test trigger": {
"main": [
[
{
"node": "Sample lead intake",
"type": "main",
"index": 0
}
]
]
},
"Sample lead intake": {
"main": [
[
{
"node": "Qualify lead decision",
"type": "main",
"index": 0
}
]
]
},
"Qualify lead decision": {
"main": [
[
{
"node": "Needs human approval?",
"type": "main",
"index": 0
}
]
]
},
"Needs human approval?": {
"main": [
[
{
"node": "Queue hot lead for approval",
"type": "main",
"index": 0
}
],
[
{
"node": "Route nurture lead",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": []
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment