How to Build a Lemlist MCP Server with n8n (AI Agent for Cold Email & Lead Enrichment)

Your AI agent can write the perfect cold email sequence — but then it hits a wall trying to actually push that sequence into Lemlist. Copy-paste, tab-switching, manual API calls: the friction kills the whole point of having an agent. What if your AI agent could just ask n8n to create the lead, enrich the contact, check campaign stats, and manage unsubscribes — all in one conversation?

That’s exactly what this workflow does. It turns your n8n instance into a Lemlist MCP Server — a live endpoint that exposes 15 Lemlist operations as tools any AI agent (Claude, ChatGPT, a custom LangChain agent) can call directly using the Model Context Protocol.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. An n8n workflow that runs as a persistent MCP server, exposing 15 Lemlist tools via a single webhook endpoint
  2. AI agents connect to the endpoint and can call any tool — create a lead, enrich a contact by LinkedIn URL, pull campaign stats, add emails to the unsubscribe list, and more
  3. Each tool call routes to the correct Lemlist API operation and returns structured data back to the agent in real time
  4. The server handles all five functional areas: Activities, Campaigns, Lead Enrichment, Lead CRUD, Team info, and Unsubscribe list management

How It Works — The Big Picture

The workflow has one entry point — the MCP Trigger node — which acts as an HTTP endpoint your agent connects to. Every tool in the workflow is wired to this trigger via an ai_tool connection. When the agent calls a tool by name, n8n routes the request to the matching Lemlist node, executes it, and streams the result back to the agent.

┌─────────────────────────────────────────────────────────────────────┐
│  LEMLIST MCP SERVER                                                  │
│                                                                      │
│  AI Agent (Claude / ChatGPT / LangChain)                             │
│       │                                                              │
│       ▼  MCP Protocol (HTTP SSE)                                     │
│  [Lemlist Tool MCP Server Trigger]  ◄──── All tools connect here    │
│       │                                                              │
│       ├──(ai_tool)── [Get many activities]                           │
│       ├──(ai_tool)── [Get many campaigns]                            │
│       ├──(ai_tool)── [Get campaign stats]                            │
│       ├──(ai_tool)── [Fetches a previously completed enrichment]     │
│       ├──(ai_tool)── [Enrich a lead using email or LinkedIn URL]     │
│       ├──(ai_tool)── [Enrich a person using email or LinkedIn URL]   │
│       ├──(ai_tool)── [Create a lead]                                 │
│       ├──(ai_tool)── [Delete a lead]                                 │
│       ├──(ai_tool)── [Get a lead]                                    │
│       ├──(ai_tool)── [Unsubscribe a lead]                            │
│       ├──(ai_tool)── [Get a team]                                    │
│       ├──(ai_tool)── [Get team credits]                              │
│       ├──(ai_tool)── [Add an email to an unsubscribe list]           │
│       ├──(ai_tool)── [Delete an email from an unsubscribe list]      │
│       └──(ai_tool)── [Get many unsubscribed emails]                  │
│                                                                      │
│  Result → streamed back to AI agent via MCP protocol                 │
└─────────────────────────────────────────────────────────────────────┘

What You’ll Need

  • n8n — self-hosted (v1.0+) or n8n Cloud. The @n8n/n8n-nodes-langchain.mcpTrigger node is required; it ships with n8n 1.40+.
  • Lemlist account — any paid plan gives you API access. A free trial works for testing.
  • Lemlist API key — found in your Lemlist account settings under Integrations → API
  • An AI agent that supports MCP — Claude Desktop, a LangChain agent, or any framework implementing the Model Context Protocol client

Estimated build time: 20–30 minutes from scratch, or under 10 minutes with the template.

Understanding MCP — What It Means for Your Workflow

If you haven’t built an MCP server before, here’s the quick version: MCP (Model Context Protocol) is an open standard that lets AI agents discover and call external tools over HTTP. Think of it as a structured API layer specifically designed for AI agents — the agent asks “what tools do you have?”, the server responds with a list of tool names and descriptions, and the agent calls whichever tools it needs.

In n8n, the mcpTrigger node acts as the server. Every Lemlist Tool node you connect to it becomes a named tool the agent can call. The beauty of this approach is that you don’t need to write any glue code — n8n handles the protocol, the routing, and the response formatting automatically.

💡

Tip: MCP is different from a standard webhook. The trigger keeps a persistent Server-Sent Events (SSE) connection open, which means agents get responses in real time rather than polling. Make sure your n8n instance is accessible from wherever your agent runs.

Building the Workflow — Step by Step

1 Add the MCP Trigger (Lemlist Tool MCP Server)

This is the entry point for every agent request. Search for “MCP Trigger” (listed as @n8n/n8n-nodes-langchain.mcpTrigger) and add it to the canvas. No parameters to configure — the node generates its own endpoint URL automatically when you activate the workflow.

Once active, the node will display an endpoint URL that looks like:

https://your-n8n-instance.com/mcp/YOUR_WEBHOOK_ID/sse

Copy this URL — you’ll paste it into your AI agent’s MCP server configuration.

💡

Tip: On self-hosted n8n, make sure your instance’s webhook URL is set correctly in the N8N_WEBHOOK_URL environment variable. Otherwise the endpoint URL shown in the node won’t be externally reachable by your agent.

2 Configure Your Lemlist Credential

Before adding any Lemlist Tool nodes, create the credential once — every node in the workflow reuses the same credential. Go to Settings → Credentials → New and select Lemlist API. Paste your Lemlist API key (from Lemlist → Settings → Integrations → API) into the field and save.

📌

Note: Lemlist uses a simple API key (not OAuth). Your key looks like lem_api_xxxxxxxxxxxxxxxx. Keep it private — it has full account access.

3 Activities — Get many activities

Add a Lemlist Tool node. Under Resource, select Activity and the operation Get Many. Attach your Lemlist credential. Connect this node to the MCP Trigger via the ai_tool output port (drag from the trigger’s bottom connector to this node).

When an agent calls this tool, it gets a list of recent outreach activities across all campaigns:

{
  "type": "emailSent",
  "leadEmail": "james.carter@outlook.com",
  "campaignId": "cam_abc123",
  "campaignName": "Q2 SaaS Outreach",
  "date": "2026-04-10T14:32:00Z",
  "sequenceStep": 1
}

4 Campaigns — Get many campaigns & Get campaign stats

Add two more Lemlist Tool nodes. The first uses Resource: Campaign, Operation: Get Many — this lists all campaigns with their status and settings. The second uses Resource: Campaign, Operation: Get Stats — this returns performance metrics for a specific campaign ID.

A typical campaign stats response looks like:

{
  "campaignId": "cam_abc123",
  "campaignName": "Q2 SaaS Outreach",
  "emailsSent": 142,
  "opened": 89,
  "openRate": 0.627,
  "clicked": 31,
  "clickRate": 0.218,
  "replied": 18,
  "replyRate": 0.127,
  "bounced": 3
}

Connect both nodes to the MCP Trigger via ai_tool connections.

💡

Tip: An AI agent with access to campaign stats can do things like “find my worst-performing campaign this month and draft a subject line improvement” — the kind of analysis that previously required manual dashboard diving.

5 Enrichment — Three enrichment tools

Add three Lemlist Tool nodes for the enrichment operations:

  • Enrich a lead using an email or LinkedIn URL — takes an email or LinkedIn profile URL and returns company, job title, and social data for an existing Lemlist lead
  • Enrich a person using an email or LinkedIn URL — same enrichment but for a person not yet in Lemlist (no lead record needed)
  • Fetches a previously completed enrichment — retrieves a cached enrichment result by ID without consuming a new credit

A typical enrichment response contains:

{
  "email": "emily.rodriguez@techcorp.com",
  "firstName": "Emily",
  "lastName": "Rodriguez",
  "linkedinUrl": "https://linkedin.com/in/emilyrodriguez",
  "companyName": "TechCorp Solutions",
  "jobTitle": "VP of Engineering",
  "city": "Austin",
  "state": "TX",
  "country": "US",
  "companySize": "51-200",
  "industry": "Software"
}

Connect all three to the MCP Trigger.

6 Lead Management — Four lead operations

Add four Lemlist Tool nodes for full CRUD on leads:

  • Create a lead — adds a new lead to a specified campaign
  • Get a lead — fetches full lead data including custom variables and campaign membership
  • Delete a lead — permanently removes a lead from a campaign
  • Unsubscribe a lead — marks a lead as unsubscribed across all campaigns

When an agent creates a lead, the minimum required fields are the lead’s email address and a campaign ID. Optional fields include first name, last name, company, phone, and any Lemlist custom variables you’ve defined.

{
  "email": "michael.chen@startupco.io",
  "firstName": "Michael",
  "lastName": "Chen",
  "companyName": "StartupCo",
  "phone": "+1 512 555 0193",
  "customVariable": "CEO"
}

7 Team Info — Get team & Get team credits

Add two Lemlist Tool nodes for team-level information. Get a team returns your Lemlist workspace details. Get team credits is the more useful one — it tells the agent how many enrichment credits remain on your plan. An agent can call this before running a bulk enrichment to check whether you have enough credits, preventing unexpected quota errors mid-run.

{
  "teamId": "team_xxxxxxx",
  "teamName": "Carter & Associates Sales",
  "enrichmentCreditsUsed": 847,
  "enrichmentCreditsTotal": 2000,
  "enrichmentCreditsRemaining": 1153
}

8 Unsubscribe List — Three blocklist operations

Add the final three Lemlist Tool nodes:

  • Add an email to an unsubscribe list — permanently blocks an email from future Lemlist outreach
  • Delete an email from an unsubscribe list — removes a mistakenly blocked address
  • Get many unsubscribed emails — retrieves the full blocklist for auditing

This is especially useful in agentic workflows where the agent monitors replies and automatically adds opt-out responses to the unsubscribe list in real time — no manual cleanup required.

Connecting to Your AI Agent

Once you’ve activated the workflow, you need to point your AI agent at the MCP endpoint. The exact steps depend on your agent framework:

Claude Desktop

Open ~/.claude/claude_desktop_config.json (macOS/Linux) or %APPDATA%\Claude\claude_desktop_config.json (Windows) and add a server entry:

{
  "mcpServers": {
    "lemlist": {
      "url": "https://your-n8n-instance.com/mcp/YOUR_WEBHOOK_ID/sse"
    }
  }
}

Restart Claude Desktop. The Lemlist tools will now appear in Claude’s tool panel and it can call them during any conversation.

LangChain / Custom Agent

Use the langchain-mcp-adapters package (Python) or any MCP-compatible client library. Point it at the same SSE URL:

from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "lemlist": {
        "url": "https://your-n8n-instance.com/mcp/YOUR_WEBHOOK_ID/sse",
        "transport": "sse"
    }
})

tools = await client.get_tools()
# tools now includes all 15 Lemlist operations

The 15 Exposed Tools — Reference Table

Tool Name Category What It Does
Get many activities Activities List recent outreach activity events
Get many campaigns Campaigns List all Lemlist campaigns
Get campaign stats Campaigns Open/click/reply rates for a campaign
Fetches a previously completed enrichment Enrichment Retrieve cached enrichment by ID
Enrich a lead using an email or LinkedIn URL Enrichment Enrich an existing Lemlist lead
Enrich a person using an email or LinkedIn URL Enrichment Enrich any email/LinkedIn URL (no lead record needed)
Create a lead Leads Add a new lead to a campaign
Get a lead Leads Fetch full lead record including custom fields
Delete a lead Leads Remove a lead from a campaign
Unsubscribe a lead Leads Mark lead as unsubscribed globally
Get a team Team Fetch workspace/team details
Get team credits Team Check remaining enrichment credits
Add an email to an unsubscribe list Blocklist Block an email from all outreach
Delete an email from an unsubscribe list Blocklist Remove a blocked email
Get many unsubscribed emails Blocklist Audit the full blocklist

Full System Flow

┌──────────────────────────────────────────────────────────────────────────────┐
│  FULL SYSTEM: AI AGENT ↔ LEMLIST MCP SERVER ↔ LEMLIST API                   │
│                                                                              │
│  User ──► AI Agent (Claude / GPT / LangChain)                                │
│               │                                                              │
│               │  "Enrich emily.rodriguez@techcorp.com and create her         │
│               │   as a lead in the Q2 SaaS Outreach campaign"               │
│               │                                                              │
│               ▼                                                              │
│  [MCP Client] ──SSE──► [n8n MCP Trigger]                                     │
│                               │                                              │
│               ┌───────────────┤                                              │
│               │               │                                              │
│               ▼               ▼                                              │
│  Tool call 1:           Tool call 2:                                         │
│  Enrich a person        Create a lead                                        │
│  (LinkedIn URL)         (email + campaign)                                   │
│       │                       │                                              │
│       ▼                       ▼                                              │
│  Lemlist Enrichment API  Lemlist Leads API                                   │
│       │                       │                                              │
│       └───────────┬───────────┘                                              │
│                   ▼                                                          │
│           Results streamed back to agent via MCP                             │
│                   │                                                          │
│                   ▼                                                          │
│  Agent responds to user: "Emily has been enriched (VP of Engineering,        │
│  TechCorp Solutions, Austin TX) and added to Q2 SaaS Outreach."              │
└──────────────────────────────────────────────────────────────────────────────┘

Testing Your Workflow

  1. Activate the workflow in n8n — the MCP trigger node will display the SSE endpoint URL
  2. Copy the endpoint URL and add it to your AI agent’s MCP server config
  3. In your agent, ask: “List all my Lemlist campaigns” — this calls Get many campaigns and should return your campaign list
  4. Test enrichment: “Enrich the person at emily.rodriguez@outlook.com” — should return job title, company, and social data
  5. Test lead creation: “Create a lead for michael.chen@startupco.io in campaign [your campaign ID]”
Problem Likely Cause Fix
Agent can’t reach the MCP endpoint n8n webhook URL not set / firewall blocking port Set N8N_WEBHOOK_URL env var; ensure port 443/5678 is open
“401 Unauthorized” from Lemlist tools API key not attached to nodes Open each Lemlist Tool node and select your Lemlist credential
Enrichment returns no data Email not found in Lemlist’s enrichment database Try a LinkedIn URL instead; not all emails have enrichment coverage
Agent only sees some tools, not all 15 Not all nodes connected to MCP Trigger Check each Lemlist Tool node has an ai_tool wire going to the trigger
MCP Trigger node missing from n8n n8n version older than 1.40 Upgrade n8n to 1.40+ — the mcpTrigger node ships with that release

Frequently Asked Questions

Do all 15 tools need separate Lemlist credentials?

No — one Lemlist API key covers all nodes. Create the credential once in n8n’s credential manager, then select it in each Lemlist Tool node. All nodes in this workflow use the same single credential.

Can I use this with n8n Cloud or only self-hosted?

Both work. On n8n Cloud your instance is already public, so the MCP endpoint is immediately reachable by external agents. On self-hosted n8n, you need to make sure your instance’s webhook URL is publicly accessible and that N8N_WEBHOOK_URL is correctly configured.

How many enrichment credits does calling the enrichment tools use?

Each call to Enrich a lead or Enrich a person uses one Lemlist enrichment credit. Fetches a previously completed enrichment retrieves a cached result and does not use a credit. Use Get team credits to monitor your remaining balance before bulk enrichment runs.

Can I add more Lemlist operations to this server later?

Yes. Just add another Lemlist Tool node to the canvas, configure it with the operation you need, and connect it to the MCP Trigger via an ai_tool wire. The server automatically advertises all connected tools to agents — no additional configuration needed.

Is the MCP connection secure? Should I worry about unauthorized access?

The MCP endpoint URL acts as a secret — anyone with the URL can call the tools. Keep it private and don’t share it publicly. For additional security, you can add an API key header check using n8n’s webhook authentication settings on the trigger node, or restrict access to specific IP ranges at the network level.

What happens if the n8n workflow is inactive when the agent tries to call a tool?

The agent will receive a connection error — the MCP SSE endpoint only works when the workflow is active. Make sure the workflow is toggled on in n8n. If you’re on self-hosted n8n, also ensure the n8n process itself is running (consider setting it up as a system service so it restarts automatically).


🚀 Get the Lemlist MCP Server Template

Skip the manual setup — get the ready-to-import n8n workflow JSON, a step-by-step Setup Guide PDF, and a Credentials Guide PDF that walks you through getting your Lemlist API key and connecting your agent.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted · All 15 Lemlist tools included

What’s Next?

  • Build a full sales agent: Combine this MCP server with a web scraping tool and an AI agent that finds prospects on LinkedIn, enriches them, and adds them to Lemlist campaigns — completely autonomously
  • Auto-manage unsubscribes: Connect a Gmail or Outlook trigger that monitors for “unsubscribe” or “opt out” keywords in replies, then calls this server to add the sender to your global blocklist
  • Campaign reporting bot: Build a Telegram or Slack bot that lets your team query campaign stats in plain English — the agent calls Get campaign stats and formats a readable report
  • Credit monitoring alert: Set up a daily n8n schedule that calls Get team credits and sends a Slack notification when enrichment credits drop below a threshold
  • Extend with other MCP servers: Combine this server with a HubSpot MCP server or a Slack MCP server so your agent can enrich a lead in Lemlist AND create a CRM contact AND notify your sales team — all in one conversation
n8n
Lemlist
MCP Server
AI Agents
Cold Email
Lead Enrichment
Sales Automation
Model Context Protocol

How to Add LinkedIn Post Commenters to HubSpot CRM with n8n

You publish a LinkedIn post, it takes off, and suddenly forty people you’ve never met are commenting on it. Each one of them is a warm lead — someone who already cares about what you have to say. But by the time you’ve finished scrolling through the comments, copying names, and looking people up one by one, the momentum is gone. What if every single one of those commenters appeared in your CRM automatically, complete with their email, job title, and company — ready for your sales team to follow up?

That’s exactly what you’ll build in this guide. Using n8n, Apify, and HubSpot, you’ll create a workflow that scrapes commenters from any LinkedIn post, enriches their profiles with professional data, and pushes qualified contacts straight into your CRM. No manual data entry, no copy-pasting, no missed opportunities.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. You paste a LinkedIn post URL into a simple web form hosted by n8n.
  2. The workflow scrapes every comment on that post and extracts the commenter’s LinkedIn profile URL.
  3. Each commenter’s profile is enriched through Apify — pulling their email address, job title, company name, city, and country.
  4. If a valid email is found, the workflow creates or updates a contact in HubSpot CRM with all the enriched data.
  5. Commenters without a discoverable email are silently skipped, keeping your CRM clean.

How It Works — The Big Picture

The entire process runs through a single n8n workflow with seven core nodes. Here’s the high-level flow:

┌──────────────────────────────────────────────────────────────────────────────────────┐
│  LINKEDIN COMMENTERS → HUBSPOT CRM                                                  │
│                                                                                      │
│  [Form Trigger]                                                                      │
│       │                                                                              │
│       ▼                                                                              │
│  [Scrape Post Comments]  ──▶  [Loop Over Commenters]                                 │
│                                     │                                                │
│                                     ▼                                                │
│                              [Wait 3 sec]                                            │
│                                     │                                                │
│                                     ▼                                                │
│                              [Enrich Profile]                                        │
│                                     │                                                │
│                                     ▼                                                │
│                              [Extract Fields]                                        │
│                                     │                                                │
│                                     ▼                                                │
│                              [Has Valid Email?]                                      │
│                               ╱            ╲                                         │
│                           YES ╱              ╲ NO                                    │
│                             ╱                  ╲                                     │
│                [Create/Update           [Skip — No Email]                            │
│                 HubSpot Contact]              │                                      │
│                        │                      │                                      │
│                        └───── ◀── Loop ──◀────┘                                      │
└──────────────────────────────────────────────────────────────────────────────────────┘
  

What You’ll Need

  • n8n instance — self-hosted or n8n Cloud (all nodes are built-in, no community nodes required)
  • Apify account — the free tier gives you $5/month of compute, enough to process around 50–100 profiles per run. Sign up here.
  • HubSpot account — free CRM tier works perfectly. You’ll need a Private App Token with crm.objects.contacts.write scope.

Estimated build time: 30–40 minutes from scratch, or under 10 minutes with the template.

Building the Workflow Step by Step

1 Submit LinkedIn Post URL (Form Trigger)

The workflow starts with n8n’s built-in Form Trigger node. This creates a simple web form where you paste the LinkedIn post URL. When you submit the form, the workflow fires.

  1. Add a Form Trigger node to your canvas.
  2. Set the Form Title to LinkedIn Post Engagement Capture.
  3. Add one form field: label it LinkedIn Post URL, set it as required, and add a placeholder like https://www.linkedin.com/posts/username_topic-activity-123...
  4. Save the node. n8n will generate a unique form URL you can bookmark.

After submission, the data flowing out of this node looks like:

{
  "LinkedIn Post URL": "https://www.linkedin.com/posts/james-carter_ai-automation-activity-7291234567890-AbCd",
  "submittedAt": "2026-04-08T14:22:00.000Z"
}
💡

Tip: You can find the URL of any LinkedIn post by clicking the three dots on the post and selecting “Copy link to post.” Make sure you’re copying the full URL, not a shortened one.

2 Scrape Post Comments (HTTP Request → Apify)

This node sends the LinkedIn post URL to Apify’s LinkedIn Post Comments Scraper actor, which returns an array of all commenters with their profile URLs and comment text.

  1. Add an HTTP Request node and connect it to the Form Trigger.
  2. Set the Method to POST.
  3. Set the URL to: https://api.apify.com/v2/acts/curious_coder~linkedin-post-comments-scraper/run-sync-get-dataset-items?token={{ $credentials.httpHeaderAuth.value }}
  4. Under Body, select JSON and enter:
    {
      "postUrl": "{{ $json['LinkedIn Post URL'] }}",
      "maxComments": 100
    }
  5. Set the Timeout to 120000 ms (2 minutes) — scraping takes time.
  6. Under Credentials, add your Apify API token as an HTTP Header Auth credential (header name: Authorization, value: Bearer YOUR_TOKEN). Alternatively, the token in the URL query parameter handles auth directly.

The response is an array of comment objects. Each item looks like:

{
  "profileUrl": "https://www.linkedin.com/in/emily-rodriguez-marketing",
  "commenterName": "Emily Rodriguez",
  "commentText": "This is such a great breakdown! We've been looking at something similar.",
  "timestamp": "2026-04-07T09:15:00.000Z"
}
💡

Tip: The maxComments parameter caps how many comments are scraped. For posts with hundreds of comments, start with 50 to test, then increase once you’ve confirmed the workflow runs smoothly.

3 Loop Over Commenters (Split In Batches)

The comment scraper returns all comments as an array. The Loop node processes them one at a time, which is important because the enrichment step hits an external API that has rate limits.

  1. Add a Split In Batches node and connect it to the HTTP Request node.
  2. Set Batch Size to 1 — we process one commenter per iteration.

The loop feeds each individual commenter object to the next node in sequence.

4 Wait Between Requests (Wait Node)

Adding a 3-second pause between enrichment requests prevents you from hitting Apify’s rate limits and keeps the workflow running reliably even with large comment batches.

  1. Add a Wait node after the loop’s “process” output.
  2. Set Amount to 3 and Unit to seconds.
📌

If you’re on Apify’s paid plan with higher rate limits, you can reduce this to 1 second or remove it entirely.

5 Enrich Profile (HTTP Request → Apify)

This is where the magic happens. For each commenter, we call Apify’s LinkedIn Profile Scraper to pull their full professional details — email, headline, company, location, and more.

  1. Add another HTTP Request node.
  2. Set the Method to POST.
  3. Set the URL to: https://api.apify.com/v2/acts/curious_coder~linkedin-profile-scraper/run-sync-get-dataset-items?token={{ $credentials.httpHeaderAuth.value }}
  4. Under Body, set JSON to:
    {
      "profileUrls": ["{{ $json.profileUrl }}"]
    }
  5. Set the Timeout to 120000 ms.
  6. Use the same Apify credential as Step 2.

The enriched profile data comes back looking something like this:

{
  "firstName": "Emily",
  "lastName": "Rodriguez",
  "email": "emily.rodriguez@techcorp.com",
  "headline": "VP of Marketing at TechCorp",
  "company": "TechCorp Inc.",
  "city": "Austin",
  "country": "US",
  "url": "https://www.linkedin.com/in/emily-rodriguez-marketing",
  "connections": 2847
}

6 Extract Profile Fields (Set Node)

The enrichment response contains dozens of fields. This Set node extracts only the ones you actually need for your CRM, creating a clean, standardized data object.

  1. Add a Set node and connect it to the Enrich Profile node.
  2. Switch to Manual mode.
  3. Map these fields:
    Output Field Expression
    email ={{ $json.email }}
    firstName ={{ $json.firstName }}
    lastName ={{ $json.lastName }}
    jobTitle ={{ $json.headline }}
    company ={{ $json.company }}
    city ={{ $json.city }}
    country ={{ $json.country }}
    linkedinUrl ={{ $json.url }}

After this node, each item has a clean, flat structure:

{
  "email": "emily.rodriguez@techcorp.com",
  "firstName": "Emily",
  "lastName": "Rodriguez",
  "jobTitle": "VP of Marketing at TechCorp",
  "company": "TechCorp Inc.",
  "city": "Austin",
  "country": "US",
  "linkedinUrl": "https://www.linkedin.com/in/emily-rodriguez-marketing"
}

7 Has Valid Email? (IF Node)

Not every LinkedIn profile has a publicly discoverable email address. This IF node checks whether the enrichment found a valid email before attempting to create a CRM contact.

  1. Add an IF node.
  2. Add two conditions (AND):
    • {{ $json.email }} exists
    • {{ $json.email }} is not empty

The true branch goes to HubSpot. The false branch goes to a No-Op node that loops back, silently skipping the commenter.

💡

Tip: Apify typically finds emails for 40–60% of LinkedIn profiles. If you need higher hit rates, consider adding a secondary enrichment service like Hunter.io or Apollo as a fallback before the IF node.

8 Create or Update HubSpot Contact (HubSpot Node)

The final action node. It takes the enriched profile data and creates a new contact in HubSpot — or updates an existing one if a contact with the same email already exists.

  1. Add a HubSpot node.
  2. Set Authentication to App Token.
  3. Set the Email field to ={{ $json.email }}.
  4. Under Additional Fields, map:
    HubSpot Field Expression
    First Name ={{ $json.firstName }}
    Last Name ={{ $json.lastName }}
    Job Title ={{ $json.jobTitle }}
    Company Name ={{ $json.company }}
    City ={{ $json.city }}
    Country ={{ $json.country }}
    Website ={{ $json.linkedinUrl }}

Connect both the HubSpot node’s output and the “Skip — No Email” node back to the Loop node to continue processing the next commenter.

📌

The HubSpot node uses upsert behavior by default — if a contact with the same email already exists, it updates their fields instead of creating a duplicate. This keeps your CRM clean even if you run the workflow on multiple posts where the same people comment.

The Data Flow

Here’s what a contact record looks like as it moves through the system, from raw LinkedIn comment to polished CRM entry:

Stage Data Available Example
After Comment Scrape Profile URL, Name, Comment Text emily-rodriguez-marketing
After Enrichment + Email, Headline, Company, City, Country emily.rodriguez@techcorp.com, VP of Marketing
After Field Extraction Clean 8-field object ready for CRM Flat JSON with all mapped fields
In HubSpot Full contact record with source tracking Contact card with LinkedIn URL as website

Full System Flow

┌──────────────────────────────────────────────────────────────────────────────┐
│                                                                              │
│   USER                     n8n WORKFLOW                      HUBSPOT CRM     │
│                                                                              │
│   Pastes URL ──▶ [Form Trigger]                                              │
│                       │                                                      │
│                       ▼                                                      │
│                  [HTTP Request] ──▶ Apify Comments API                       │
│                       │                                                      │
│                       ▼                                                      │
│                  [Loop: 1 at a time]                                          │
│                       │                                                      │
│                       ▼                                                      │
│                  [Wait 3 sec]                                                │
│                       │                                                      │
│                       ▼                                                      │
│                  [HTTP Request] ──▶ Apify Profile API                        │
│                       │                                                      │
│                       ▼                                                      │
│                  [Set: Extract Fields]                                        │
│                       │                                                      │
│                       ▼                                                      │
│                  [IF: Email exists?]                                          │
│                    ╱        ╲                                                 │
│                YES            NO                                             │
│                 ╱                ╲                                            │
│   [HubSpot: Upsert]     [Skip: No-Op]                                       │
│         │                      │           ──▶  Contact created/updated      │
│         └──── Loop back ◀──────┘                                             │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘
  

Testing Your Workflow

  1. Find a test post. Use one of your own LinkedIn posts that has at least 5–10 comments. Avoid posts with hundreds of comments for your first test — keep it small.
  2. Open the form. Click “Test workflow” in n8n, then open the form URL in your browser. Paste the LinkedIn post URL and submit.
  3. Watch the execution. In n8n, you’ll see the workflow run node by node. The comment scraping takes 30–60 seconds. Each profile enrichment takes another 10–30 seconds.
  4. Check HubSpot. Open your HubSpot contacts list and look for the newly created records. Verify that the name, email, job title, and company are populated correctly.
  5. Review skipped contacts. Check the workflow execution log — any commenters without emails will show as passing through the “Skip — No Email” branch.
Problem Likely Cause Fix
No comments returned Post URL is incorrect or post has no comments Copy the URL directly from the post’s share menu; make sure the post is public
Enrichment returns empty data Apify token is invalid or has run out of credits Check your Apify dashboard for remaining credits and regenerate the token
HubSpot returns 401 error App Token doesn’t have the right scopes In HubSpot, edit your Private App and ensure crm.objects.contacts.write is enabled
Workflow times out Too many comments + enrichment is slow Reduce maxComments to 50, or increase the HTTP Request timeout to 180000 ms
Duplicate contacts in CRM Email field is mapped incorrectly Make sure the HubSpot node’s Email field uses the exact expression ={{ $json.email }}

Frequently Asked Questions

Does this work with n8n Cloud or only self-hosted?

It works with both. Every node in this workflow is a standard n8n built-in node — no community nodes required. The Apify calls are made through regular HTTP Request nodes, so there’s nothing extra to install.

How many comments can it handle per run?

The template is set to scrape up to 100 comments per post. You can increase this by changing the maxComments parameter, but keep in mind that each profile enrichment uses Apify compute credits. A batch of 100 profiles typically costs around $1–2 on the free tier.

What if a commenter is already in my HubSpot CRM?

The HubSpot node uses upsert logic — it matches on email address. If a contact with that email already exists, their record gets updated with the latest data instead of creating a duplicate. Your CRM stays clean no matter how many times you run it.

Can I use a different CRM instead of HubSpot?

Yes. Swap the HubSpot node for any CRM node that n8n supports — Salesforce, Pipedrive, Zoho CRM, or even a Google Sheets node if you want a lightweight approach. The enrichment pipeline stays the same; you just change the final destination.

Is scraping LinkedIn comments against their terms of service?

Apify handles the data collection through their compliant infrastructure. The data accessed is publicly visible comment information. That said, always review LinkedIn’s current terms and your local data protection regulations before using any automation at scale.

What percentage of profiles actually have an email?

Apify’s LinkedIn Profile Scraper typically discovers email addresses for 40–60% of profiles, depending on the industry and how complete people’s profiles are. B2B professionals in tech and marketing tend to have higher hit rates.

Get the LinkedIn Commenters to HubSpot CRM Template

Skip the 40-minute build. Get the pre-built workflow JSON, step-by-step setup guide, and credentials walkthrough — import it into n8n and start capturing leads in under 10 minutes.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add a Slack notification — get a message in your team channel every time a new contact is added to HubSpot, with their name, company, and the post they commented on.
  • Tag contacts by post topic — use a Set node to add a custom HubSpot property that records which post the contact engaged with, so your sales team knows what they’re interested in.
  • Chain with an email sequence — connect HubSpot to your email tool (Mailchimp, SendGrid, or HubSpot’s own sequences) to automatically send a welcome email to new contacts.
  • Schedule it to run on multiple posts — replace the Form Trigger with a Schedule Trigger and a list of post URLs in a Google Sheet to process several posts on autopilot.
n8n
LinkedIn
HubSpot
Apify
CRM
Lead Generation
Contact Enrichment
automation

How to Auto-Create HubSpot Contacts from Typeform Submissions with n8n

Every Typeform submission sitting in a spreadsheet instead of your CRM is a lead going cold. Someone just told you their name, email, company, and job title — and that data is gathering dust in a tab you’ll check “later.” Meanwhile, your sales team has no idea the lead exists. This workflow fixes that permanently. You’ll build an n8n automation that catches every Typeform submission the moment it arrives, maps the fields to HubSpot contact properties, checks whether the person already exists in your CRM, and either creates a new contact or updates the existing one — all without you lifting a finger.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. A visitor fills out your Typeform (contact form, demo request, lead magnet — whatever you use).
  2. n8n instantly receives the submission via webhook and extracts the relevant fields: email, first name, last name, company, phone, and job title.
  3. The workflow searches HubSpot to check if a contact with that email already exists.
  4. If the contact exists, their record gets updated with the latest information. If they’re new, a fresh contact is created in HubSpot with all the form data.
  5. Your sales team sees the new or updated contact in HubSpot immediately — no manual entry, no lag, no missed leads.

How It Works — The Big Picture

The entire workflow runs as a single chain with one decision branch. Here’s the full flow at a glance:

┌──────────────────────────────────────────────────────────────────────────────┐
│  AUTO-CREATE HUBSPOT CONTACTS FROM TYPEFORM                                 │
│                                                                              │
│  [Typeform Trigger] → [Map Fields] → [Check If Email Exists] → [Contact?]  │
│                                                          ↓ Yes      ↓ No    │
│                                              [Update Contact]  [Create New] │
└──────────────────────────────────────────────────────────────────────────────┘

What You’ll Need

  • n8n instance — self-hosted or n8n Cloud (any plan)
  • Typeform account — free tier works, but you need at least one published form
  • HubSpot account — free CRM tier is enough; you’ll need an OAuth2 app or private app token
  • 5 minutes if you use the template, or about 25–30 minutes building from scratch

Estimated build time: 25–30 minutes from scratch, or under 10 minutes with the template.

Building the Workflow — Step by Step

1 Typeform Trigger (Typeform Trigger Node)

This node listens for new Typeform submissions in real time. Every time someone hits “Submit” on your form, n8n receives the full response payload instantly.

  1. In n8n, add a new node and search for Typeform Trigger.
  2. Connect your Typeform credential (API token — see the Credentials Guide for how to get one).
  3. In the Form dropdown, select the form you want to connect.
  4. Click Listen for Test Event, then go to your Typeform and submit a test response.

After the test submission, you’ll see the raw data from Typeform:

{"What is your email address?":"james.carter@gmail.com","What is your first name?":"James","What is your last name?":"Carter","What company do you work for?":"Ridgeline Analytics","What is your phone number?":"(555) 867-5309","What is your job title?":"VP of Marketing","submittedAt":"2026-04-07T14:23:00.000Z"}
💡

Tip: Typeform uses the full question text as the field key. If you rename a question later, update the field references in the Map Fields node too.

2 Map Fields (Set Node)

Typeform’s field names are long question strings. This Set node transforms them into clean field names that match HubSpot’s default properties.

Output Field Expression Purpose
email {{ $json['What is your email address?'] }} Primary identifier for HubSpot
firstname {{ $json['What is your first name?'] }} Contact first name
lastname {{ $json['What is your last name?'] }} Contact last name
company {{ $json['What company do you work for?'] }} Company association
phone {{ $json['What is your phone number?'] }} Phone number
jobtitle {{ $json['What is your job title?'] }} Job title property
lead_source Typeform (static) Tracks lead origin
💡

Tip: If your Typeform uses different question text, update the expressions to match. The output field names should stay the same — they match HubSpot’s defaults.

3 Check If Email Exists (HubSpot Node — Search)

Before creating a contact, this node searches HubSpot by email address to check for duplicates.

  1. Add a HubSpot node. Set Resource to Contact, Operation to Search.
  2. Add a filter: Property = email, Operator = Equal, Value = {{ $json.email }}

4 Contact Exists? (IF Node)

Routes the flow: if {{ $json.total }} is greater than 0 → update the existing contact. Otherwise → create a new one.

📌

The IF node’s output 0 (true) goes to Update, output 1 (false) goes to Create. Don’t mix them up.

5 Update Existing Contact (HubSpot Node — Update)

Connected to the true branch. Uses {{ $('Check If Email Exists').item.json.results[0].id }} as the Contact ID, and pulls field values from the Map Fields node.

6 Create New Contact (HubSpot Node — Create)

Connected to the false branch. Sets the email and all additional fields from the Map Fields node to create a fresh HubSpot contact.

Full System Flow

┌────────────────────────────────────────────────────────────────────────────────────────┐
│  VISITOR SUBMITS TYPEFORM                                                              │
│       ↓                                                                                │
│  [Typeform Trigger] ─── receives webhook payload ──→ [Map Fields]                     │
│                                                          ↓                             │
│                                               [Check If Email Exists]                  │
│                                                          ↓                             │
│                                                  [Contact Exists?]                     │
│                                                    ↓ YES    ↓ NO                       │
│                                           [Update Contact] [Create Contact]            │
└────────────────────────────────────────────────────────────────────────────────────────┘

Testing Your Workflow

  1. Activate the workflow in n8n by toggling the Active switch.
  2. Submit a test form response in Typeform using a test email not in HubSpot yet.
  3. Check HubSpot — you should see a new contact with all fields populated.
  4. Submit again with the same email but different company name.
  5. Check HubSpot again — the contact should show the updated company.
Problem Likely Cause Fix
Workflow never triggers Webhook not registered Deactivate and reactivate the workflow. Verify webhook in Typeform settings.
HubSpot 401 error Expired OAuth token Re-authorize your HubSpot credential. Check scopes include contacts read+write.
Empty fields in HubSpot Question text mismatch Compare Typeform question keys with Map Fields expressions exactly.
Duplicate contacts IF node misconfigured Verify condition checks $json.total > 0. True → Update, False → Create.

Frequently Asked Questions

Can I use this with HubSpot’s free CRM?

Yes. The free CRM includes full contact management and API access. Default fields like email, name, company, and phone are all available on the free plan.

What if the submission is missing some fields?

The workflow still runs — HubSpot leaves empty fields blank. It won’t overwrite existing data with empty values on updates.

Can I auto-add contacts to a HubSpot list or deal?

Yes — add a HubSpot node after Create New Contact set to “Add Contact to List” or “Create Deal”. The template covers core contact creation; lists and deals are easy extensions.

Does this work with Typeform’s free plan?

Yes, with the 10 responses/month limit. Webhooks work on all Typeform plans including free.

What if I use Google Forms or Tally instead?

Swap the Typeform Trigger for a Google Forms Trigger or Webhook node. Update the Map Fields expressions for the new payload. The HubSpot side stays identical.

Will this slow down my Typeform?

No. Typeform sends the webhook after submission is complete. The respondent sees the Thank You screen immediately. n8n processes asynchronously.

Get the HubSpot + Typeform Template

Skip the 30-minute build. Get the complete workflow JSON, setup guide, and credentials walkthrough — import into n8n and be live in under 10 minutes.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Auto-assign leads to sales reps — round-robin assignment based on territory or workload.
  • Send a Slack notification — ping #new-leads when a contact is created.
  • Enroll in a HubSpot sequence — automated welcome email within minutes.
  • Score leads with AI — use OpenAI to evaluate company and job title before CRM entry.
n8n
HubSpot
Typeform
CRM
lead capture
automation
no-code