---
title: "Automate Lead Qualification Fast with n8n & Pipedrive"
slug: "automated-lead-qualification-n8n-pipedrive"
published: "2026-02-05"
updated: "2026-04-06"
categories:
  - "Tools"
tags:
  - "automated lead qualification"
  - "n8n workflows"
  - "Pipedrive automation"
  - "lead intake webhook"
  - "CRM deduplication"
  - "lead qualification pipeline"
  - "Pipedrive custom fields"
  - "email notifications n8n"
  - "webhook gateway"
  - "lead enrichment"
llm-intent: "reference"
audience-level: "intermediate"
framework-versions:
  - "n8n@2.x"
  - "pipedrive-api@v1"
  - "docker@24.x"
  - "nodejs@20.x"
  - "smtp-standard"
status: "stable"
llm-purpose: "Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…"
llm-prereqs:
  - "Access to n8n"
  - "Access to Pipedrive"
  - "Access to SMTP"
  - "Access to HTTP API"
  - "Access to JavaScript (n8n Code node)"
llm-outputs:
  - "Completed outcome: Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…"
---

**Summary Triples**
- (architecture, composed_of, three interconnected n8n workflows: webhook gateway, CRM creation sub-flow, email notification sub-flow)
- (webhook_gateway, responsibility, receive inbound leads, deduplicate, and orchestrate CRM and email sub-flows)
- (deduplication, implemented_with, Pipedrive search API calls (email or name+company heuristics) performed from n8n before creating records)
- (crm_subflow, creates, contact, organization, link between contact and organization, and follow-up activity in Pipedrive)
- (custom_fields, populated_by, mapping incoming form/chat answers to Pipedrive custom fields via n8n Set or Code nodes)
- (email_notifications, sent_via, n8n SMTP node: internal formatted summary to sales and optional visitor confirmation)
- (data_transforms, performed_with, n8n Code node using JavaScript for sanitization, normalization, and mapping)
- (workflow_invoke, connected_via, n8n Execute Workflow node to call CRM and email sub-flows from the webhook gateway)
- (testing, uses, curl/Postman or ngrok + local n8n instance to POST sample lead JSON to webhook endpoint)
- (deployment, recommended, run n8n in Docker with environment variables for API tokens and SMTP credentials; enforce HTTPS for webhooks)

### {GOAL}
Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…

### {PREREQS}
- Access to n8n
- Access to Pipedrive
- Access to SMTP
- Access to HTTP API
- Access to JavaScript (n8n Code node)

### {STEPS}
1. Prepare Pipedrive custom fields
2. Build CRM creation sub-flow
3. Build email notification sub-flow
4. Build webhook gateway
5. Test end-to-end flow
6. Secure and deploy

<!-- llm:goal="Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…" -->
<!-- llm:prereq="Access to n8n" -->
<!-- llm:prereq="Access to Pipedrive" -->
<!-- llm:prereq="Access to SMTP" -->
<!-- llm:prereq="Access to HTTP API" -->
<!-- llm:prereq="Access to JavaScript (n8n Code node)" -->
<!-- llm:output="Completed outcome: Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…" -->

# Automate Lead Qualification Fast with n8n & Pipedrive
> Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…
Matija Žiberna · 2026-02-05

I was building a lead qualification system for a client project where the website chatbot and contact form needed to do more than collect data. Every inbound lead needed to arrive in Pipedrive fully structured — contact created, company linked, qualification fields populated, a follow-up activity assigned, and the sales team notified with a formatted summary. All of it before a human ever looked at the lead.

The manual version of this process was taking fifteen to twenty minutes per lead. Search the CRM for duplicates, create the contact, create the company, link them together, copy the qualification answers into custom fields, write a summary note, create a follow-up task, send an internal email. Multiply that by twenty leads a day and you have someone's entire morning consumed by data entry.

I built the automated version with three interconnected n8n workflows that handle the full pipeline: a CRM structuring sub-flow that builds complete Pipedrive records, an email notification sub-flow that dispatches internal alerts and visitor confirmations, and a main webhook gateway that receives inbound leads, prevents duplicates, and orchestrates the other two.

This guide walks through building all three workflows from scratch. By the end, you will have a production-ready system where every lead that hits your webhook arrives in Pipedrive fully qualified, linked, and actionable — with your sales team notified immediately.

## What we are building

The system has three workflows that call each other. Understanding the architecture before building prevents confusion later.

**Workflow 1: CRM Creation Sub-Flow.** This is the foundation. It receives structured lead data and builds a complete Pipedrive record — Person, Organisation, Lead, Activity, structured Note, and custom qualification fields. It does not have its own trigger. Other workflows call it.

**Workflow 2: Email Notification Sub-Flow.** This is the messaging layer. It receives a payload with an event type and lead data, routes to the correct email template, sends an internal notification to the sales inbox, and optionally sends an acknowledgement to the visitor. Like Workflow 1, it is called by other workflows.

**Workflow 3: Webhook Gateway.** This is the public entry point. It receives POST requests from a chatbot or form, normalizes the data, checks for duplicate contacts in Pipedrive, and branches based on whether the lead is new or an update. For new leads, it calls Workflow 1 and Workflow 2. For updates, it modifies the existing record directly.

The reason for three separate workflows instead of one giant flow is maintainability. The CRM creation logic lives in one place. If you need to add a field to every new lead, you change it once. The email templates live in one place. If you add a new notification type, you add one Switch branch. The gateway stays clean because it only handles routing and deduplication.

> [!NOTE]
> This tutorial assumes you have a working n8n instance and a Pipedrive account with API access. If you need to set up n8n first, follow my guide on [self-hosting n8n on a VPS with Docker](/blog/self-host-n8n-vps-guide). You will also need SMTP credentials for sending notification emails.

## Preparing Pipedrive custom fields

Before building any workflows, you need custom fields in Pipedrive to store qualification data. Go to Pipedrive Settings, then Data Fields, and create the following custom fields on the Person entity:

- `contact_source` (text or single option) — tracks where the lead originated
- `budget_readiness` (text) — the lead's budget status
- `decision_authority` (text) — whether they are the decision maker
- `project_type` (text) — what kind of project they need
- `scope_scale` (text) — the size and complexity of the project
- `project_timeline` (text) — when they want to start

After creating each field, note down the custom field API key. Pipedrive assigns each custom field a hash-based key that looks something like `a4b2c8d1e5f3a7b9`. You will need these keys when configuring the workflow nodes that write to these fields.

> [!TIP]
> To find custom field keys in Pipedrive, go to Settings, then Data Fields, click on a field, and look at the API key in the field details panel. Alternatively, call the Pipedrive API endpoint `GET /v1/personFields` to get all fields with their keys programmatically.

## Workflow 1: CRM creation sub-flow

Create a new workflow in n8n and name it something clear like "SUB — Create Lead + Person + Organisation". This workflow will be called by the gateway, not triggered directly.

### Step 1: Add the Execute Workflow Trigger

Start with the **Execute Workflow Trigger** node. This tells n8n that this workflow is a sub-flow, invoked by another workflow using the Execute Workflow node.

The trigger will receive structured JSON input with these fields:

```json
{
  "firstName": "Jane",
  "lastName": "Smith",
  "email": "jane@example.com",
  "phone": "+1234567890",
  "company": "Acme Corp",
  "priority": "high",
  "contact_source": "website_chatbot",
  "status": "qualified",
  "message": "Looking for a CMS rebuild with AI search integration",
  "reasoning": "Clear budget, decision maker, timeline under 3 months",
  "recommendedAction": "Schedule discovery call this week",
  "budget_readiness": "Approved budget, 50-100k range",
  "project_type": "Website rebuild with AI layer",
  "project_timeline": "Q2 2026",
  "decision_authority": "CEO, final decision maker",
  "scope_scale": "Mid-size, 3 month engagement"
}
```

You do not need to configure anything on this node beyond placing it. The data structure is defined by whatever the calling workflow sends.

### Step 2: Create the Person in Pipedrive

Add a **Pipedrive** node set to the "Create" operation on the "Person" resource.

Configure it as follows:

- **Name:** Set this using an expression: `{{ $json.firstName }} {{ $json.lastName }}`
- **Email:** `{{ $json.email }}`
- **Phone:** `{{ $json.phone }}`
- **Additional Fields:** Add your `contact_source` custom field key and map it to `{{ $json.contact_source }}`

This node outputs the newly created Person record, including the critical `id` field that every subsequent node needs.

### Step 3: Attach a structured note

Add another **Pipedrive** node, this time set to "Create" on the "Note" resource.

The note content should be an HTML-formatted summary that gives sales reps instant context. Build it as an expression:

```html
<h3>Lead Summary</h3>
<p><strong>Status:</strong> {{ $json.status }}</p>
<p><strong>Priority:</strong> {{ $json.priority }}</p>
<hr>
<h4>Contact</h4>
<p>{{ $json.firstName }} {{ $json.lastName }}<br>
{{ $json.email }}<br>
{{ $json.phone }}</p>
<hr>
<h4>Project Scope</h4>
<p>{{ $json.message }}</p>
<hr>
<h4>Why This Lead Matters</h4>
<p>{{ $json.reasoning }}</p>
<hr>
<h4>Qualification Details</h4>
<p><strong>Budget:</strong> {{ $json.budget_readiness }}<br>
<strong>Timeline:</strong> {{ $json.project_timeline }}<br>
<strong>Decision Authority:</strong> {{ $json.decision_authority }}<br>
<strong>Project Type:</strong> {{ $json.project_type }}<br>
<strong>Scope:</strong> {{ $json.scope_scale }}</p>
<hr>
<h4>Recommended Next Step</h4>
<p>{{ $json.recommendedAction }}</p>
```

Set the **Person ID** field to the output of the previous Create Person node: `{{ $('Create Person').item.json.id }}`

The purpose of this note is that a sales rep can open the contact and understand everything about the lead in thirty seconds without digging through raw form data.

### Step 4: Create a follow-up activity

Add a **Pipedrive** node set to "Create" on the "Activity" resource.

- **Subject:** `New lead — {{ $json.firstName }} {{ $json.lastName }}`
- **Type:** `call`
- **Due Date:** Use an expression for today's date: `{{ $now.format('yyyy-MM-dd') }}`
- **Person ID:** `{{ $('Create Person').item.json.id }}`

This ensures every single lead generates a same-day action item. No lead sits untouched because someone forgot to check the pipeline.

### Step 5: Create the Organisation

Add a **Pipedrive** node set to "Create" on the "Organization" resource.

- **Name:** `{{ $json.company || 'Unknown Company' }}`

The fallback string prevents the node from failing when the company field is empty. Some leads — especially from chatbot interactions — might not provide a company name immediately.

Here is the critical configuration: set the node's **On Error** behavior to **Continue (using regular output)**. Organisation creation can fail for various reasons — duplicate names, API limits, missing data — and you do not want the entire lead pipeline to stop because the company record could not be created. The Person and Lead are more important than the Organisation link.

### Step 6: Associate the Person with the Organisation

Add a **Pipedrive** node set to "Update" on the "Person" resource.

- **Person ID:** `{{ $('Create Person').item.json.id }}`
- **Organisation ID:** `{{ $('Create Organisation').item.json.id }}`

This links the contact to their company inside Pipedrive. If the Organisation creation failed in the previous step but continued, this node will receive a null organisation ID and fail gracefully — which is the correct behavior.

### Step 7: Create the Lead record

Add a **Pipedrive** node set to "Create" on the "Lead" resource.

- **Title:** `NEW LEAD — {{ $json.firstName }} {{ $json.lastName }} || {{ $json.company }}`
- **Person ID:** `{{ $('Create Person').item.json.id }}`
- **Organisation ID:** `{{ $('Create Organisation').item.json.id }}`

The title format makes leads scannable in the Pipedrive pipeline view. Every lead follows the same naming convention, which matters when the volume starts growing.

### Step 8: Update the lead source channel

Pipedrive's Lead resource has a `channel` property that tracks where the lead came from, but the native n8n Pipedrive node does not expose it. Use an **HTTP Request** node instead.

- **Method:** PATCH
- **URL:** `https://api.pipedrive.com/v1/leads/{{ $('Create Lead').item.json.data.id }}`
- **Authentication:** Use your Pipedrive API token as a query parameter or header
- **Body (JSON):**

```json
{
  "channel": 57
}
```

Replace `57` with the channel ID that matches your lead source in Pipedrive. You can find channel IDs through the Pipedrive API documentation or by inspecting existing leads.

> [!NOTE]
> Using an HTTP Request node instead of the native Pipedrive node is a common pattern in n8n. When the built-in node does not expose a specific API field, drop down to a raw HTTP request. The Pipedrive REST API covers everything the node does not.

### Step 9: Update qualification custom fields on the Person

Add a final **HTTP Request** node to update the Person record with all qualification data.

- **Method:** PUT
- **URL:** `https://api.pipedrive.com/v1/persons/{{ $('Create Person').item.json.id }}`
- **Body (JSON):**

```json
{
  "YOUR_BUDGET_FIELD_KEY": "{{ $json.budget_readiness }}",
  "YOUR_AUTHORITY_FIELD_KEY": "{{ $json.decision_authority }}",
  "YOUR_PROJECT_TYPE_FIELD_KEY": "{{ $json.project_type }}",
  "YOUR_SCOPE_FIELD_KEY": "{{ $json.scope_scale }}",
  "YOUR_TIMELINE_FIELD_KEY": "{{ $json.project_timeline }}"
}
```

Replace each `YOUR_*_FIELD_KEY` placeholder with the actual custom field keys you noted earlier from Pipedrive settings.

This is the step that makes qualification data structured and reportable inside your CRM. Instead of qualification answers being buried in a note or a raw JSON blob, they exist as proper fields you can filter, segment, and report on.

That completes Workflow 1. Save it. The full sequence is: create Person, attach Note, create Activity, create Organisation, link Person to Organisation, create Lead, set Lead channel, enrich Person with qualification fields. Nine steps that replace fifteen minutes of manual CRM data entry.

## Workflow 2: Email notification sub-flow

Create a new workflow and name it "SUB — Email Notification". This workflow handles all internal sales notifications and visitor acknowledgement emails. Having it as a separate sub-flow means any workflow in your system can send notifications without duplicating email templates.

### Step 1: Add the Execute Workflow Trigger

Same as Workflow 1, start with the **Execute Workflow Trigger** node. This sub-flow expects a payload with these common fields:

```json
{
  "eventType": "sales_inquiry_notification",
  "firstName": "Jane",
  "lastName": "Smith",
  "email": "jane@example.com",
  "phone": "+1234567890",
  "company": "Acme Corp",
  "priority": "high",
  "category": "website_rebuild",
  "reasoning": "Clear budget and timeline, decision maker confirmed",
  "message": "Full project description here",
  "timestamp": "2026-02-15T10:30:00Z",
  "qualifier_1_key": "budget_readiness",
  "qualifier_1_label": "Budget Status",
  "qualifier_1_answer": "Approved, 50-100k range",
  "qualifier_2_key": "decision_authority",
  "qualifier_2_label": "Decision Authority",
  "qualifier_2_answer": "CEO, final decision maker",
  "qualifier_3_key": "project_timeline",
  "qualifier_3_label": "Timeline",
  "qualifier_3_answer": "Q2 2026"
}
```

The `eventType` field is the routing key. Everything else is template data.

### Step 2: Route by event type

Add a **Switch** node that evaluates `{{ $json.eventType }}`.

Create three output branches:

- **Output 1:** Value equals `sales_inquiry_notification`
- **Output 2:** Value equals `vendor_request`
- **Output 3:** Value equals `form_submission`

Each branch leads to a different email template node. This is where the sub-flow architecture pays off — adding a fourth notification type later means adding one Switch rule and one email node.

### Step 3: Build the sales inquiry notification email

For the `sales_inquiry_notification` branch, add a **Send Email** node (using SMTP credentials).

- **From:** `no-reply@yourdomain.com`
- **To:** `sales@yourdomain.com` (your internal sales inbox)
- **Subject:** Build this as an expression: `NEW {{ $json.priority.toUpperCase() }} PRIORITY LEAD — {{ $json.company }}`

For the **HTML body**, build a formatted template that sales can scan in seconds:

```html
<div style="font-family: Arial, sans-serif; max-width: 600px;">
  <h2 style="color: #1a1a1a;">New Lead Received</h2>

  <table style="width: 100%; border-collapse: collapse;">
    <tr>
      <td style="padding: 8px; font-weight: bold;">Company</td>
      <td style="padding: 8px;">{{ $json.company }}</td>
    </tr>
    <tr>
      <td style="padding: 8px; font-weight: bold;">Contact</td>
      <td style="padding: 8px;">{{ $json.firstName }} {{ $json.lastName }}</td>
    </tr>
    <tr>
      <td style="padding: 8px; font-weight: bold;">Email</td>
      <td style="padding: 8px;">{{ $json.email }}</td>
    </tr>
    <tr>
      <td style="padding: 8px; font-weight: bold;">Phone</td>
      <td style="padding: 8px;">{{ $json.phone }}</td>
    </tr>
    <tr>
      <td style="padding: 8px; font-weight: bold;">Priority</td>
      <td style="padding: 8px;">{{ $json.priority }}</td>
    </tr>
    <tr>
      <td style="padding: 8px; font-weight: bold;">Category</td>
      <td style="padding: 8px;">{{ $json.category }}</td>
    </tr>
  </table>

  <h3 style="margin-top: 20px;">Why This Lead Matters</h3>
  <p>{{ $json.reasoning }}</p>

  <h3>Key Qualifications</h3>
  <table style="width: 100%; border-collapse: collapse;">
    <tr>
      <td style="padding: 8px; font-weight: bold;">{{ $json.qualifier_1_label }}</td>
      <td style="padding: 8px;">{{ $json.qualifier_1_answer }}</td>
    </tr>
    <tr>
      <td style="padding: 8px; font-weight: bold;">{{ $json.qualifier_2_label }}</td>
      <td style="padding: 8px;">{{ $json.qualifier_2_answer }}</td>
    </tr>
    <tr>
      <td style="padding: 8px; font-weight: bold;">{{ $json.qualifier_3_label }}</td>
      <td style="padding: 8px;">{{ $json.qualifier_3_answer }}</td>
    </tr>
  </table>

  <p style="color: #666; font-size: 12px; margin-top: 20px;">
    Received: {{ $json.timestamp }}
  </p>
</div>
```

This template renders cleanly in every major email client because it uses inline styles and basic HTML tables. No external CSS, no web fonts, no fancy layout that Gmail will strip.

### Step 4: Build the other notification templates

For the `vendor_request` branch, add another **Send Email** node with a template tailored for partnership inquiries. The structure is similar but the fields differ — vendor requests typically include `requestType`, `description`, `budget`, and `deadline` instead of qualification answers.

For the `form_submission` branch, add a third **Send Email** node for generic contact form submissions. Simpler template, fewer qualification fields, but the same core structure of contact details plus message.

I will not reproduce every template line by line here because the pattern is identical — an HTML table with inline styles, customized field labels, and expression-based values. The important thing is that each event type gets its own template so sales can visually distinguish a hot lead notification from a vendor request at a glance.

### Step 5: Check if visitor email exists

After all three notification branches, connect them to a single **IF** node.

The condition checks whether the visitor's email address is present:

- **Value 1:** `{{ $('When Executed by Another Workflow').item.json.email }}`
- **Operation:** is not empty

This gate prevents the workflow from trying to send an acknowledgement email when the email field is missing or blank. Chatbot interactions sometimes collect leads progressively — name and company first, email later. Without this check, the SMTP node would throw an error.

### Step 6: Send visitor acknowledgement

On the **true** branch of the IF node, add a **Send Email** node.

- **From:** `noreply@yourdomain.com`
- **To:** `{{ $('When Executed by Another Workflow').item.json.email }}`
- **Subject:** `We have received your request`
- **Body:**

```html
<div style="font-family: Arial, sans-serif; max-width: 600px;">
  <p>Hi {{ $('When Executed by Another Workflow').item.json.firstName }},</p>
  <p>Thank you for reaching out. We have received your inquiry
     and will follow up shortly.</p>
  <p>Best regards,<br>The Team</p>
</div>
```

This single acknowledgement node is shared across all three event types. No matter how the lead came in — chatbot, vendor form, contact form — the visitor gets the same confirmation. This is the advantage of converging all branches into one IF gate before the acknowledgement step.

Save Workflow 2. The complete structure is: trigger receives data, Switch routes by event type, three parallel email nodes send internal notifications, all branches converge into an IF check, and a single node sends the visitor confirmation if an email exists.

## Workflow 3: The webhook gateway

This is the main workflow — the public entry point that receives lead data from your website and orchestrates everything else. Create a new workflow and name it "Lead Intake — Webhook Gateway".

### Step 1: Set up the Webhook trigger

Add a **Webhook** node.

- **HTTP Method:** POST
- **Path:** Something descriptive like `lead-intake`

This gives you a URL like `https://your-n8n-instance.com/webhook/lead-intake` that your chatbot or form backend will POST to.

The webhook expects a JSON body with this structure:

```json
{
  "firstName": "Jane",
  "lastName": "Smith",
  "email": "jane@example.com",
  "phone": "+1234567890",
  "company": "Acme Corp",
  "priority": "high",
  "status": "qualified",
  "category": "website_rebuild",
  "reasoning": "Clear budget and timeline confirmed",
  "recommendedAction": "Schedule discovery call",
  "actionType": "new_lead",
  "qualifierAnswers": [
    { "question": { "key": "budget_readiness" }, "answer": "Approved, 50-100k" },
    { "question": { "key": "decision_authority" }, "answer": "CEO" },
    { "question": { "key": "project_type" }, "answer": "Website rebuild" },
    { "question": { "key": "scope_scale" }, "answer": "Mid-size" },
    { "question": { "key": "project_timeline" }, "answer": "Q2 2026" }
  ]
}
```

The `actionType` field controls whether this is a new lead or an update to an existing one. The `qualifierAnswers` array is dynamic — it supports any number of questions with arbitrary keys.

> [!WARNING]
> In production, protect this webhook endpoint. Add a shared secret header (e.g., `x-webhook-secret`) and validate it in a Code node before processing any data. Without authentication, anyone who discovers the URL can inject fake leads into your CRM.

### Step 2: Normalize qualifier answers

The `qualifierAnswers` array is flexible for the frontend but inconvenient for CRM mapping. Add a **Code** node to flatten it into a simple key-value object.

```javascript
// File: n8n Code Node — Map Qualifier Answers
const input = $input.first().json;
const answers = input.qualifierAnswers || [];

const results = {};

for (const qa of answers) {
  const key = qa.question?.key;
  if (key) {
    results[key] = qa.answer;
  }
}

return [{
  json: {
    ...input,
    ...results,
  }
}];
```

This takes the dynamic array and spreads each answer as a top-level field. After this node, the data has both the original `qualifierAnswers` array (for reference) and flat fields like `budget_readiness`, `decision_authority`, `project_type`, `scope_scale`, and `project_timeline` available as direct expressions.

The reason for this transformation is that every downstream node — Pipedrive fields, email templates, sub-flow inputs — needs to reference these values by key. Having them at the top level of the JSON object means you can write `{{ $json.budget_readiness }}` instead of looping through an array in every expression.

### Step 3: Branch by action type

Add a **Switch** node that checks `{{ $json.actionType }}`.

- **Output 1:** Value equals `new_lead`
- **Output 2:** Value equals `update_lead`

These two branches handle fundamentally different operations. New leads go through the full creation pipeline. Updates modify an existing record without creating duplicates.

### Path A: Handling new leads

#### Step 4A: Search for existing person by email

Before creating anything, check if this person already exists in Pipedrive. Add a **Pipedrive** node set to "Search" on the "Person" resource.

- **Term:** `{{ $json.email }}`
- **Search by:** `email`

This is duplicate prevention. If someone submits a form twice, or a chatbot conversation generates a lead that was already captured through a different channel, you do not want duplicate Person records in your CRM.

#### Step 5A: Branch on search results

Add an **IF** node that checks whether the search returned results.

- **Condition:** `{{ $('Search Person').item.json.data?.items?.length > 0 }}`

If the person exists, you take the "already exists" path. If not, you take the "create new" path.

#### Step 6A (Person exists): Attach a note and create an activity

If the person already exists, you do not create a new record. Instead, you enrich the existing one.

Add a **Pipedrive** node to create a Note on the existing person. The HTML content follows the same structure as in Workflow 1 — status, contact details, project scope, reasoning, qualification answers, and recommended next step. The Person ID comes from the search result: `{{ $('Search Person').item.json.data.items[0].item.id }}`

Then add a **Pipedrive** node to create an Activity linked to the same Person ID with today's date and a "call" type.

This means repeat inquiries from the same person still generate actionable context in the CRM, just without creating duplicate contacts.

#### Step 7A (Person exists): Notify sales

After enriching the existing person, call Workflow 2 to send the notification. Add an **Execute Workflow** node.

Before calling it, you may need a second **Code** node to flatten the qualifier answers onto the root object again, this time including the qualifier labels for the email template:

```javascript
// File: n8n Code Node — Prepare Email Payload
const input = $input.first().json;
const answers = input.qualifierAnswers || [];

const emailData = { ...input };

answers.forEach((qa, index) => {
  const num = index + 1;
  emailData[`qualifier_${num}_key`] = qa.question?.key || '';
  emailData[`qualifier_${num}_label`] = qa.question?.label || qa.question?.key || '';
  emailData[`qualifier_${num}_answer`] = qa.answer || '';
});

emailData.eventType = 'sales_inquiry_notification';

return [{ json: emailData }];
```

This prepares the data in the exact shape that Workflow 2 expects — flat qualifier fields with numbered keys (`qualifier_1_label`, `qualifier_1_answer`, etc.) and the `eventType` set.

Connect this Code node to an **Execute Workflow** node that calls your "SUB — Email Notification" workflow.

#### Step 8A (Person does not exist): Call the CRM sub-flow

If the search returned no results, the person is new. Add an **Execute Workflow** node that calls your "SUB — Create Lead + Person + Organisation" workflow (Workflow 1).

Pass the full flattened data object. Workflow 1 handles everything — Person creation, Organisation, Lead, Activity, Note, custom fields.

After Workflow 1 completes, add another **Execute Workflow** node to call Workflow 2 for the notification email. The data preparation is identical to Step 7A.

### Path B: Handling lead updates

The update path is simpler. When `actionType` is `update_lead`, the chatbot or form is sending additional information about an existing lead — perhaps they answered more qualification questions in a follow-up conversation.

#### Step 4B: Search for the person by email

Same as Step 4A — search Pipedrive for the person by email.

#### Step 5B: Update the person record

If the person exists, add a **Pipedrive** node set to "Update" on the "Person" resource.

Update whatever fields have changed. Typically this means the qualification custom fields:

- **Person ID:** From search results
- **Custom Fields:** Map the flattened qualifier values to their Pipedrive field keys

If the person does not exist (which should be rare on an update action), you can either fall through to the new lead creation path or log the anomaly and skip processing.

> [!TIP]
> The update path is particularly useful when lead qualification happens progressively. A chatbot might capture name and company in the first interaction, then budget and timeline in a follow-up. Each interaction sends an `update_lead` action that enriches the existing CRM record without creating duplicates.

### Connecting the workflows

To wire the Execute Workflow nodes, you need the workflow IDs of your two sub-flows. In n8n, open each sub-flow, look at the URL — the number at the end is the workflow ID. Enter that ID in the Execute Workflow node's "Workflow" field.

Make sure both sub-flows are active. If a sub-flow is inactive, the Execute Workflow node will fail.

## Testing the complete pipeline

Test the system by sending a POST request to your webhook URL. You can use the n8n Webhook test mode or send a request with curl:

```bash
curl -X POST https://your-n8n-instance.com/webhook/lead-intake \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "jane@example.com",
    "phone": "+1234567890",
    "company": "Acme Corp",
    "priority": "high",
    "status": "qualified",
    "category": "website_rebuild",
    "actionType": "new_lead",
    "reasoning": "Clear budget, decision maker, short timeline",
    "recommendedAction": "Schedule discovery call this week",
    "qualifierAnswers": [
      { "question": { "key": "budget_readiness" }, "answer": "Approved, 50-100k" },
      { "question": { "key": "decision_authority" }, "answer": "CEO, final decision maker" },
      { "question": { "key": "project_type" }, "answer": "Website rebuild with AI" },
      { "question": { "key": "scope_scale" }, "answer": "Mid-size, 3 months" },
      { "question": { "key": "project_timeline" }, "answer": "Q2 2026" }
    ]
  }'
```

After execution, verify in Pipedrive that you see a new Person with the correct name, email, and phone. Check that an Organisation named "Acme Corp" exists and is linked to the Person. Confirm the Lead record exists with the correct title format. Open the Person and verify the structured Note is attached and readable. Check that a call Activity is assigned for today. Inspect the Person's custom fields and confirm all five qualification values are populated. Finally, check your sales inbox for the notification email.

Send the same request again with the same email. This time, the gateway should detect the duplicate, skip the creation sub-flow, and instead attach a new Note and Activity to the existing Person.

Then send an update:

```bash
curl -X POST https://your-n8n-instance.com/webhook/lead-intake \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "actionType": "update_lead",
    "qualifierAnswers": [
      { "question": { "key": "project_timeline" }, "answer": "Moved to Q1 2026" }
    ]
  }'
```

Verify that the Person's `project_timeline` custom field updates without creating new records.

## What you have at the end

After building these three workflows, your system handles the full lead lifecycle automatically.

Every lead that hits the webhook gets normalized, deduplicated, and routed. New leads arrive in Pipedrive as fully structured records with Person, Organisation, Lead, Activity, and Note all linked together, plus structured qualification data in custom fields that are filterable and reportable. Returning leads get enriched with new context instead of creating duplicates. Your sales team gets a formatted email notification the moment a lead arrives, with enough context to decide on a next step without opening the CRM first. And visitors get an immediate acknowledgement that their inquiry was received.

The manual version of this process took fifteen minutes per lead. The automated version takes under three seconds.

The sub-flow architecture means the system is modular. When you need to add a new qualification field, you update the custom field mapping in one place. When you add a new lead source — a second chatbot, a partner referral form, a different landing page — you point it at the same webhook and everything downstream works the same way.

If you want to understand how this lead pipeline connects to a broader CMS-driven automation system where content changes, document processing, and AI workflows all run through the same architecture, I covered that in [How Payload CMS and n8n Turn a Website Into an Operational System](/blog/payload-cms-n8n-operational-website-system). And for the background on why self-hosted n8n is a better foundation for this kind of work than Zapier, read my [n8n vs Zapier comparison](/blog/n8n-vs-zapier-automation-for-serious-businesses).

Let me know in the comments if you have questions, and subscribe for more practical development guides.

Thanks, Matija

## LLM Response Snippet
```json
{
  "goal": "Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…",
  "responses": [
    {
      "question": "What does the article \"Automate Lead Qualification Fast with n8n & Pipedrive\" cover?",
      "answer": "Automated lead qualification using n8n & Pipedrive: step-by-step tutorial to build a webhook gateway, CRM sub-flows, and email notifications—deploy in…"
    }
  ]
}
```