How to Automate Lead Enrichment and Routing with n8n
This guide builds a real-time n8n workflow that fires when a contact is created in HubSpot, enriches the lead through the Clearbit API, writes the extra fields back, scores the lead with a simple formula, and alerts Slack when the score is high.
The Problem
A new lead usually arrives with an email and little else, and n8n can fill the gap. Sales reps then spend time on LinkedIn and company websites to learn whether the person is worth calling, and the hottest leads wait in the same queue as the weakest. Manual research does not scale, and by the time a rep finds the VP at a mid size software company, a competitor may have replied. An n8n workflow can fetch the company size, industry and job title the second a contact is created, apply the same scoring rules every time, and push only the strongest leads to the team.
What You Will Need
- A self-hosted n8n instance (Community Edition is free) or an n8n Cloud trial
- A HubSpot developer app plus a private app token, a Clearbit (or equivalent enrichment) API key, and a Slack app for
#sales-hot-leads - Three custom HubSpot contact properties for enriched industry, company size and LinkedIn URL
- Estimated n8n setup time: 60 to 90 minutes
Step-by-Step Workflow Setup
1 Trigger on new HubSpot contacts
The n8n workflow starts with the HubSpot Trigger node, which listens for HubSpot webhook events and starts one n8n execution for each new contact. This is the real-time entry point of the n8n flow: every lead is enriched the moment it appears in the CRM.
Add an n8n HubSpot Trigger node and configure:
Node: HubSpot Trigger
Authentication: Developer API
App ID: your HubSpot developer app ID
Developer API Key: your developer API key
Event: Contact Created
Additional Fields > Max Concurrent Requests: 5
The trigger needs a HubSpot developer app so HubSpot can call the n8n webhook URL. Create the app, add the crm.objects.contacts.read scope, and copy the n8n production webhook URL into the app's webhook settings. Remember that the Test URL only works while the editor listens.
{
"objectId": 4102,
"subscriptionType": "contact.creation",
"portalId": 1234567,
"occurredAt": 1730737200000
}
The payload holds only the contact ID. The next node fetches the email address, so add an n8n HubSpot node with Operation: Get on that contact ID, requesting the email, firstname, lastname and company properties.
2 Call the Clearbit Enrichment API with an HTTP Request node
The n8n HTTP Request node sends the lead email to the Clearbit Combined endpoint, which returns person and company data in one response. Clearbit is now part of HubSpot, so confirm which API access and keys your plan includes before you rely on it, and swap the URL if you use another provider with similar fields.
Node: HTTP Request
Method: GET
URL: https://person.clearbit.com/v2/combined/find
Send Query Parameters: On
email: {{ $json.properties.email }}
Authentication: Generic Credential Type > Header Auth
Name: Authorization
Value: Bearer sk_YOUR_CLEARBIT_KEY
Options:
Response > Never Error: On
Settings > On Error: Continue (regular output)
Setting Never Error and On Error: Continue matters. Clearbit answers 404 for unknown people and 202 when a lookup is queued. Without these settings, n8n marks the execution as failed and the lead is never scored. The n8n Code node in the next step treats both cases as empty enrichment.
{
"person": {
"name": { "fullName": "Alex Kim" },
"employment": { "title": "VP of Operations", "role": "operations", "seniority": "executive" },
"linkedin": { "handle": "in/alexkim" }
},
"company": {
"name": "Brightpath",
"category": { "industry": "Software" },
"metrics": { "employees": 240 }
}
}
3 Merge the enrichment data into the lead record
An n8n Code node flattens the nested response into the fields the CRM and the scoring step need. Optional chaining keeps the n8n node from crashing when Clearbit returns a partial profile. Name this n8n node Merge Enrichment.
Node: Code
Mode: Run Once for Each Item
const e = $json;
const contact = $('Get Contact').item.json.properties;
const title = e.person?.employment?.title || '';
const linkedinHandle = e.person?.linkedin?.handle || '';
return { json: {
contactId: $('Get Contact').item.json.id,
email: contact.email,
firstName: contact.firstname,
company: e.company?.name || contact.company || '',
jobTitle: title,
role: e.person?.employment?.role || '',
seniority: e.person?.employment?.seniority || '',
industry: e.company?.category?.industry || '',
companySize: Number(e.company?.metrics?.employees || 0),
linkedinUrl: linkedinHandle ? 'https://www.linkedin.com/' + linkedinHandle : '',
enriched: Boolean(e.person || e.company)
}};
{
"contactId": "4102",
"email": "alex@brightpath.io",
"company": "Brightpath",
"jobTitle": "VP of Operations",
"industry": "Software",
"companySize": 240,
"linkedinUrl": "https://www.linkedin.com/in/alexkim",
"enriched": true
}
Numbers are coerced with Number() so the scoring comparison later works on integers, not strings. The enriched flag lets you report in n8n on how many leads Clearbit could match.
4 Update the HubSpot contact with enriched fields
A second n8n HubSpot node writes the new fields back, so sales sees the job title, company size and LinkedIn link inside the contact record without leaving the CRM.
Node: HubSpot
Resource: Contact
Operation: Update
Contact ID: {{ $json.contactId }}
Update Fields:
Job Title: {{ $json.jobTitle }}
Company Name: {{ $json.company }}
Website URL: (leave empty)
Custom Properties:
industry_enriched: {{ $json.industry }}
company_size_enriched: {{ $json.companySize }}
linkedin_url_enriched: {{ $json.linkedinUrl }}
Create the three custom properties in HubSpot first, before you run n8n, as single line text for industry and LinkedIn and as a number for company size. Use separate enriched properties instead of overwriting form values, so you can always see what the lead typed versus what the enrichment API returned.
{
"id": "4102",
"properties": {
"jobtitle": "VP of Operations",
"industry_enriched": "Software",
"company_size_enriched": "240"
}
}
The n8n HubSpot node returns the contact, not your original fields. Reference the merge node by name in later expressions with $('Merge Enrichment').item.json.
5 Score the lead with a multi-factor formula
Another n8n Code node converts the enriched profile into a number. The formula adds points for each signal that suggests a good fit. Keep the weights in one object so a sales manager can tune them without reading the n8n logic.
Node: Code
Mode: Run Once for Each Item
const m = $('Merge Enrichment').item.json;
const target = ['software', 'saas', 'financial services', 'technology'];
let score = 0;
const reasons = [];
if (m.companySize > 100) { score += 20; reasons.push('company_size>100: +20'); }
if (/\b(vp|vice president|director)\b/i.test(m.jobTitle)) { score += 30; reasons.push('senior_role: +30'); }
if (target.includes(m.industry.toLowerCase())) { score += 25; reasons.push('industry_match: +25'); }
if (m.linkedinUrl.length > 0) { score += 5; reasons.push('has_linkedin: +5'); }
return { json: { ...m, score, hot: score >= 60, reasons } };
In n8n, the scoring formula is: score = 20 (size over 100) + 30 (VP or Director title) + 25 (industry match) + 5 (LinkedIn present), for a maximum of 80. The listed weights and the industry list are examples; replace them with the traits of your own best customers.
{
"contactId": "4102",
"score": 80,
"hot": true,
"reasons": ["company_size>100: +20", "senior_role: +30", "industry_match: +25", "has_linkedin: +5"]
}
6 Route hot leads with an IF node
An n8n IF node separates leads that deserve immediate attention from the rest. With the example weights, a score of 60 means the lead has at least a senior title plus a second strong signal.
Node: IF
Conditions:
Value 1: {{ $json.score }}
Operation: Number > Larger or Equal
Value 2: 60
The true output of the n8n IF node goes to Slack. The false output can end the workflow, or write a lower priority tag to HubSpot for nurture campaigns. The n8n IF node keeps the logic visible, and you can change the threshold in one field.
{
"score": 80,
"branch": "true"
}
7 Alert the sales channel in Slack
The final n8n Slack node posts the full enriched profile to #sales-hot-leads, so a rep can reply within minutes with context. n8n sends the message as soon as scoring ends.
Node: Slack
Resource: Message
Operation: Send
Send Message To: Channel
Channel: sales-hot-leads
Text: Hot lead (score {{ $json.score }}): *{{ $json.firstName }}* at {{ $json.company }}
Title: {{ $json.jobTitle }}
Industry: {{ $json.industry }} | Size: {{ $json.companySize }} employees
LinkedIn: {{ $json.linkedinUrl }}
Why: {{ $json.reasons.join(', ') }}
HubSpot: https://app.hubspot.com/contacts/YOUR_PORTAL_ID/contact/{{ $json.contactId }}
{
"ok": true,
"channel": "C10HOTLEAD",
"ts": "1730737305.000500"
}
Including the reasons list shows the rep why the lead scored high, which builds trust in the n8n model and exposes weights that need adjusting. Activate the n8n workflow, create a test contact in HubSpot, and watch the execution complete.
Troubleshooting
The n8n HubSpot Trigger never fires
HubSpot only calls the webhook URL registered in the developer app. Make sure the app uses the n8n Production URL, the workflow is active, and the contact creation subscription is enabled. The trigger also needs the matching app ID and developer key; an ordinary private app token does not register webhooks.
Enrichment returns 404 or 202 and the n8n run stops
Unknown emails return 404, and queued lookups return 202 with no body. Turn on Never Error in the n8n HTTP Request options and set On Error to Continue, then let the Code node treat missing person and company objects as an empty profile. Free mail domains such as gmail.com usually return no company data.
Scores in n8n are always zero
The company size arrives as a string or is missing. Convert it with Number() in the merge step and confirm the optional chaining paths match your provider response. Open the n8n Code node output and check each field before the scoring node runs.
Free n8n Workflow Resources
Get started with the free workflow pack on the LightCube Labs templates page.
Production-Ready Workflow Packs
Get this workflow and more, with setup guides, editable configurations, and a shared failure-alert system.
View the Starter ($29) and Pro ($99) PacksFrequently Asked Questions
Is Clearbit still available for this n8n workflow?
Clearbit now sits inside HubSpot, so access and endpoints depend on your plan. The workflow only needs a response with title, company size, industry and LinkedIn handle, so any provider that returns those fields works after you change the URL and the field paths in the merge Code node.
How do I choose the hot lead threshold?
Start with 60, then compare the scores of past customers with past lost leads. Move the threshold in the n8n IF node until roughly the share of leads your reps can contact in a day lands in the hot branch.
Will n8n overwrite what the lead typed in the form?
No. Enrichment values go to separate custom properties, except for job title, which only updates the standard field. Keep the form value if you need an audit trail.
How is this different from batch lead scoring?
This n8n workflow enriches and scores one lead when it is created. It does not re-score existing contacts on a schedule; the n8n lead scoring workflow does that using engagement and behavior data.