How to Build AI Lead Scoring with n8n + Ollama (Free Workflow Included)
Most lead scoring tools charge $50–300/month and require sending your customer data to external servers. With n8n and Ollama, you can build a lead scoring pipeline that runs entirely on your own hardware — zero API costs, full data privacy, and unlimited leads.
In this tutorial, you'll build a webhook-triggered workflow that:
- Receives lead data via HTTP webhook
- Scores the lead 1–100 using local AI (Ollama)
- Enriches the lead with AI-generated company insights
- Routes hot leads (score > 70) for immediate follow-up
- Returns the scored lead data as a JSON response
Why AI Lead Scoring Matters
Manual lead qualification wastes sales time. Your reps spend hours reading form submissions, checking LinkedIn, and guessing which leads are worth pursuing. Most of them aren't.
AI-powered lead scoring solves this by analyzing every lead the moment it arrives:
| Signal | What AI Detects | Impact on Score |
|---|---|---|
| Business email domain | Company size, industry from domain | +15–25 points |
| Job title | Decision-maker vs. researcher | +10–30 points |
| Message content | Purchase intent, urgency, budget signals | +10–25 points |
| Company context | Industry fit, team size indicators | +5–15 points |
Why local AI works here: Lead scoring is a structured classification task — exactly what 8B-parameter models excel at. You don't need GPT-4 for this. A well-prompted Llama 3 running on your hardware gives consistent, reliable scores with zero per-request cost.
The Architecture
The workflow uses a simple but effective pipeline:
[Webhook: Lead arrives]
|
v
[Ollama: Score lead 1-100]
|
v
[Ollama: Enrich with company insights]
|
v
[Switch: Route by score]
/ \
v v
[Hot Lead] [Nurture]
(score>70) (score<=70)
Two Ollama calls per lead, each taking 2–5 seconds on modest hardware. For a business processing 50 leads/day, that's under 10 minutes of total compute time.
Prerequisites
You need n8n and Ollama installed. If you haven't set these up yet, here's the quick version:
# Install Ollama + pull model
curl -fsSL https://ollama.ai/install.sh | sh
ollama pull llama3:8b
# Run n8n in Docker
docker run -d --name n8n -p 5678:5678 \
--add-host=host.docker.internal:host-gateway \
-v n8n_data:/home/node/.n8n \
n8nio/n8n
Docker users: Use http://host.docker.internal:11434 for Ollama URLs in your workflows. If n8n runs natively (not Docker), use http://localhost:11434.
Free Workflow: AI Lead Scorer
Here's a complete, working n8n workflow. Import the JSON directly into your n8n instance.
What It Does
Exposes a POST /webhook/score-lead endpoint. Send lead data as JSON with fields like name, email, company, title, and message.
Sends the lead data to Ollama with a carefully tuned prompt that evaluates business email, job title seniority, message intent, and company fit. Returns a score from 1–100 with reasoning.
A second Ollama call generates company insights based on the email domain and any context from the message. Estimates company size, likely industry, and potential use cases.
Leads scoring above 70 are flagged as "hot" for immediate follow-up. Leads scoring 40–70 go to nurture. Below 40 are low priority.
The Workflow JSON
Click to expand full workflow JSON
{
"name": "AI Lead Scorer + Enrichment (Ollama)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "score-lead",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook",
"name": "Receive Lead",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [240, 300],
"webhookId": "score-lead"
},
{
"parameters": {
"url": "http://localhost:11434/api/generate",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ model: 'llama3:8b', prompt: 'You are a B2B lead scoring expert. Score this lead from 1-100 based on likelihood to convert to a paying customer.\\n\\nEvaluate these signals:\\n- Email domain (business email = higher score, gmail/yahoo = lower)\\n- Job title (C-suite/VP/Director = highest, intern/student = lowest)\\n- Company name (known brands = higher)\\n- Message content (mentions budget, timeline, specific needs = higher)\\n\\nLead data:\\nName: ' + ($json.body.name || 'Unknown') + '\\nEmail: ' + ($json.body.email || 'Unknown') + '\\nCompany: ' + ($json.body.company || 'Unknown') + '\\nJob Title: ' + ($json.body.title || 'Unknown') + '\\nMessage: ' + ($json.body.message || 'No message') + '\\n\\nRespond in this EXACT JSON format, nothing else:\\n{\"score\": , \"tier\": \"\", \"reason\": \"\"}', stream: false, options: { temperature: 0.2, num_predict: 200 } }) }}",
"options": { "timeout": 60000 }
},
"id": "score-lead",
"name": "Score Lead (Ollama)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 300]
},
{
"parameters": {
"url": "http://localhost:11434/api/generate",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ model: 'llama3:8b', prompt: 'Based on this lead information, provide brief company insights.\\n\\nEmail: ' + ($json.body.email || '') + '\\nCompany: ' + ($json.body.company || '') + '\\nTitle: ' + ($json.body.title || '') + '\\n\\nRespond in this EXACT JSON format:\\n{\"estimated_size\": \"\", \"likely_industry\": \"\", \"talking_points\": [\"\", \"\"]}', stream: false, options: { temperature: 0.3, num_predict: 300 } }) }}",
"options": { "timeout": 60000 }
},
"id": "enrich-lead",
"name": "Enrich Lead (Ollama)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 500]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "lead",
"name": "lead",
"value": "={{ JSON.stringify({ name: $('Receive Lead').item.json.body.name, email: $('Receive Lead').item.json.body.email, company: $('Receive Lead').item.json.body.company, title: $('Receive Lead').item.json.body.title }) }}",
"type": "string"
},
{
"id": "scoring",
"name": "scoring",
"value": "={{ $('Score Lead (Ollama)').item.json.data ? JSON.parse($('Score Lead (Ollama)').item.json.data).response : 'error' }}",
"type": "string"
},
{
"id": "enrichment",
"name": "enrichment",
"value": "={{ $('Enrich Lead (Ollama)').item.json.data ? JSON.parse($('Enrich Lead (Ollama)').item.json.data).response : 'error' }}",
"type": "string"
}
]
}
},
"id": "merge-results",
"name": "Combine Results",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [680, 400]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ lead: JSON.parse($json.lead), scoring: (() => { try { return JSON.parse($json.scoring) } catch(e) { return { raw: $json.scoring } } })(), enrichment: (() => { try { return JSON.parse($json.enrichment) } catch(e) { return { raw: $json.enrichment } } })() }) }}",
"options": {}
},
"id": "respond",
"name": "Return Scored Lead",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [900, 400]
}
],
"connections": {
"Receive Lead": {
"main": [[
{ "node": "Score Lead (Ollama)", "type": "main", "index": 0 },
{ "node": "Enrich Lead (Ollama)", "type": "main", "index": 0 }
]]
},
"Score Lead (Ollama)": {
"main": [[{ "node": "Combine Results", "type": "main", "index": 0 }]]
},
"Enrich Lead (Ollama)": {
"main": [[{ "node": "Combine Results", "type": "main", "index": 0 }]]
},
"Combine Results": {
"main": [[{ "node": "Return Scored Lead", "type": "main", "index": 0 }]]
}
},
"settings": { "executionOrder": "v1" },
"tags": [{ "name": "AI" }, { "name": "Ollama" }, { "name": "Sales" }, { "name": "Lead Scoring" }]
}
Testing the Workflow
Once imported and activated, test with curl:
curl -X POST http://localhost:5678/webhook/score-lead \
-H "Content-Type: application/json" \
-d '{
"name": "Sarah Chen",
"email": "sarah.chen@acme-corp.com",
"company": "Acme Corp",
"title": "VP of Operations",
"message": "We are evaluating workflow automation tools for our team of 50. Looking to implement in Q2 with a budget of $5-10k."
}'
Expected response:
{
"lead": {
"name": "Sarah Chen",
"email": "sarah.chen@acme-corp.com",
"company": "Acme Corp",
"title": "VP of Operations"
},
"scoring": {
"score": 88,
"tier": "hot",
"reason": "VP-level decision maker with specific budget, timeline, and team size at a business domain"
},
"enrichment": {
"estimated_size": "mid-market",
"likely_industry": "technology",
"talking_points": [
"Operations focus suggests process optimization needs",
"50-person team indicates growing organization with automation potential"
]
}
}
Extending the Workflow
This free version gives you the scoring engine. Here's how to make it production-ready:
Connect to Your CRM
Add a node after scoring to push results to HubSpot, Salesforce, or Pipedrive. n8n has native nodes for all major CRMs. The scored lead data maps directly to custom properties.
Slack/Email Alerts for Hot Leads
Add a Switch node after scoring. Route leads with score > 70 to a Slack notification or email alert so your sales team can follow up within minutes.
Store Leads in a Database
Add a PostgreSQL or Google Sheets node to log every scored lead. Track conversion rates by score tier to validate and tune your scoring model over time.
Batch Processing
Instead of webhook-triggered, set up a schedule trigger that pulls new leads from your CRM every 15 minutes and scores them in batches.
How the Prompt Engineering Works
The scoring prompt is the core of this workflow. Here's why specific choices matter:
Structured JSON output: The prompt asks for {"score": X, "tier": "Y", "reason": "Z"}. This gives you machine-parseable output that downstream nodes can work with. The temperature: 0.2 setting keeps output consistent.
Explicit scoring criteria: Rather than asking "score this lead," the prompt lists specific signals (email domain, job title, budget mentions). This makes the model's reasoning predictable and auditable.
Separate enrichment call: Scoring and enrichment are split into two Ollama calls running in parallel. This is faster than one large prompt and lets you tune each independently.
Model recommendation: llama3:8b handles this well for most leads. If you need better accuracy for complex B2B scenarios, try llama3:70b or mistral:7b-instruct. The workflow JSON works with any Ollama model — just change the model name.
Comparison: Local vs. Cloud Lead Scoring
| n8n + Ollama (Local) | Clay / Apollo / HubSpot AI | |
|---|---|---|
| Cost | $0/month (runs on your hardware) | $50–300/month |
| Data privacy | Lead data never leaves your server | Sent to third-party servers |
| Customization | Full control over scoring criteria | Limited to vendor's model |
| Lead volume | Unlimited | Tiered by plan |
| Setup time | 30 minutes | 15 minutes |
| Accuracy | Good for structured signals | Better for enrichment-heavy scoring |
The local approach wins on cost and privacy. Cloud tools win on enrichment data (they have databases of company info). For most small-to-mid businesses processing under 500 leads/day, the local approach is more than sufficient.
Want the Production-Ready Version?
The Self-Hosted AI Workflow Pack includes an advanced lead scorer with CRM integration, Slack alerts, score calibration, and 10 more AI workflows — all running locally with Ollama.
Get All 11 Workflows — $39One-time purchase. No subscriptions. 30-day money-back guarantee.
Next Steps
- Import the workflow — Copy the JSON above into n8n
- Test with sample leads — Use the curl command to send test data
- Connect to your form — Point your website's contact form at the webhook URL
- Add routing — Connect Slack, email, or CRM nodes for automatic follow-up
If you want to explore more n8n + Ollama workflows, check out our other tutorials: