Guides

How Do I Connect Leadpipe to Close CRM?

Send identified visitors into Close as Leads and Contacts, auto-create tasks for pricing-page hits, and route high-intent traffic to the right rep.

Elene Marjanidze Elene Marjanidze · · 10 min read
How Do I Connect Leadpipe to Close CRM?

Close is built for inside sales teams that actually dial. Your pipeline lives there, your sequences live there, and your reps have Smart Views running before their first coffee. The gap, like with most CRMs, is that none of the high-intent anonymous traffic on your site ever touches a Lead record.

I am Elene, and I write Leadpipe’s integration recipes. Close customers ask me this question more than any other CRM except Salesforce. This guide is the exact playbook: webhook into Close, Leads and Contacts mapped cleanly, Smart Views that surface real-time visitor intent, tasks that fire when someone hits pricing. You will be live in under half an hour.


What you will build

Every identified US B2B visitor becomes a Close Lead with contact, firmographics, and visit context attached, deduped against existing Leads, and surfaced in a “Website visitors” Smart View with a task created when the visit is high-intent.


Prerequisites

RequirementNotes
Leadpipe accountStart with 500 free identifications
Close accountBasic plan or higher for API and webhook access
Close adminTo create custom fields and Smart Views
Zapier accountOptional, for the no-code path
Time25-30 minutes

If you are weighing Close against Salesforce or HubSpot, the Close playbook below is cleaner to set up because Close’s Lead object already has the parent-child shape (Lead owns Contacts) that maps to a visitor plus the Company they work at.


The data model

Close is one of the few CRMs where the object names line up neatly with Leadpipe’s payload.

Leadpipe webhook payload

       ├─> Close Lead       (Company: Acme.io, domain, industry, employees)

       └─> Close Contact    (Person: Maria Lopez, email, phone, title)
              └─ lives inside the Lead

One Lead per company. Multiple Contacts per Lead if several people from the same company visit. This matches how Close’s UI wants you to work.


Step 1: Turn on Leadpipe webhooks

In Leadpipe, go to Settings, Integrations, Webhooks. Click Add Webhook.

  1. Destination URL: you will generate this in Step 2.
  2. Trigger type: First Match. One payload the first time a visitor is identified. If you want return visits to update records, add a second webhook with Every Update.
  3. Fields: include everything. You will filter later.
  4. Save and fire a test event.

Here is an example payload Close will receive. The payload is the same one documented in our webhook payload reference.

{
  "email": "maria.lopez@acme.io",
  "first_name": "Maria",
  "last_name": "Lopez",
  "phone": "+1-415-555-0139",
  "company_name": "Acme.io",
  "company_domain": "acme.io",
  "company_industry": "B2B SaaS",
  "company_employee_count": 180,
  "job_title": "Head of Growth",
  "seniority": "Head",
  "department": "Marketing",
  "linkedin_url": "linkedin.com/in/marialopezgrowth",
  "city": "San Francisco",
  "state": "California",
  "country": "US",
  "page_url": "/pricing",
  "pages_viewed": ["/", "/features", "/pricing"],
  "visit_duration": 241,
  "intent_score": 78,
  "matched_topics": ["sales engagement", "CRM"],
  "return_visit": false
}

Reminder on compliance. For EU and UK visitors, Leadpipe defaults to company-level identification unless the visitor has given affirmative consent. Those payloads will usually arrive with company fields and no Contact fields, which is correct, not broken.


Step 2: Set up custom fields in Close

Close gives you custom fields on both Leads and Contacts. Create these before wiring up the integration. If you rename them later, your Zapier mapping will silently break.

Lead-level custom fields (Company object in Close)

Field nameTypePurpose
DomainTextYour upsert key
IndustryTextFrom Leadpipe
Employee countNumberFrom Leadpipe
Last visit pageTextMost recent page that triggered identification
Last visit atDateWhen the visit happened
Intent scoreNumber1-100
Intent topicsTextComma-separated matched topics
Pages viewedText (long)Full session
Lead source detailTextValue: “Leadpipe Visitor ID”

Contact-level custom fields

Field nameTypePurpose
LinkedIn URLURLFast verification in the timeline
DepartmentTextMarketing, Sales, etc.
SeniorityTextIC through C-level
Return visitorCheckboxFlipped when Leadpipe sends return_visit: true
Visit duration secondsNumberPer-session engagement

To create them: Settings, Custom Fields, choose the object, Add.


Step 3: The Zapier path (under 20 minutes)

Zapier is the fastest way to get Leadpipe data into Close. The same pattern appears in our Zapier recipes post and the Pipedrive recipe.

  1. Create a new Zap. Trigger: Webhooks by Zapier, Catch Hook. Copy the URL and paste it into the Leadpipe webhook from Step 1.
  2. Fire the test event. Zapier shows all payload fields.
  3. Filter step. Suggested rule: only continue if email does not contain gmail.com, yahoo.com, outlook.com, hotmail.com and visit_duration is greater than 20.
  4. Formatter step (important). Lowercase the company_domain and strip www.. Close will happily create two Leads for acme.io and www.acme.io if you do not.
  5. Action: Close, Find or Create Lead. Search by Custom Field “Domain” equals the lowercased domain. If not found, create with company_name, Domain, Industry, Employee count, and set Status to your default (I use “Potential”).
  6. Action: Close, Find or Create Contact. Search by email. If not found, create with first_name, last_name, email, phone, job_title, linkedin_url, and attach to the Lead from Step 5.
  7. Action: Close, Update Lead. Patch Last visit page, Last visit at, Intent score, Intent topics, Pages viewed.
  8. Conditional action. If page_url contains /pricing or intent_score is over 70, create a Close Task: “Hot visitor, respond within 1 hour”, assigned to the Lead owner.

Turn the Zap on. Identified US B2B visitors should land in Close within seconds.


Step 4: Direct API path

For teams doing meaningful volume or that want custom logic (fuzzy domain match, target-account routing, enrichment before the write), skip Zapier and call Close’s REST API from a small middleware.

// POST /leadpipe-webhook
// Env: CLOSE_API_KEY
import fetch from 'node-fetch';

const authHeader =
  'Basic ' + Buffer.from(process.env.CLOSE_API_KEY + ':').toString('base64');

export async function handler(req) {
  const v = req.body;
  const domain = v.company_domain?.toLowerCase().replace(/^www\./, '');
  if (!domain) return { statusCode: 200, body: 'skipped: no domain' };

  // Find Lead by domain custom field
  const search = await fetch(
    `https://api.close.com/api/v1/lead/?query=` +
      encodeURIComponent(`custom.domain:${domain}`),
    { headers: { Authorization: authHeader } }
  );
  const { data } = await search.json();
  let leadId = data?.[0]?.id;

  // Create if missing
  if (!leadId) {
    const created = await fetch('https://api.close.com/api/v1/lead/', {
      method: 'POST',
      headers: { Authorization: authHeader, 'Content-Type': 'application/json' },
      body: JSON.stringify({
        name: v.company_name,
        url: `https://${domain}`,
        custom: {
          domain,
          industry: v.company_industry,
          employee_count: v.company_employee_count,
          lead_source_detail: 'Leadpipe Visitor ID',
        },
      }),
    });
    leadId = (await created.json()).id;
  }

  // Update visit fields
  await fetch(`https://api.close.com/api/v1/lead/${leadId}/`, {
    method: 'PUT',
    headers: { Authorization: authHeader, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      custom: {
        last_visit_page: v.page_url,
        last_visit_at: new Date().toISOString(),
        intent_score: v.intent_score,
        intent_topics: (v.matched_topics || []).join(', '),
        pages_viewed: (v.pages_viewed || []).join(', '),
      },
    }),
  });

  // Upsert Contact
  if (v.email) {
    await fetch('https://api.close.com/api/v1/contact/', {
      method: 'POST',
      headers: { Authorization: authHeader, 'Content-Type': 'application/json' },
      body: JSON.stringify({
        lead_id: leadId,
        name: `${v.first_name || ''} ${v.last_name || ''}`.trim(),
        title: v.job_title,
        emails: v.email ? [{ type: 'office', email: v.email }] : [],
        phones: v.phone ? [{ type: 'office', phone: v.phone }] : [],
        urls: v.linkedin_url ? [{ type: 'url', url: v.linkedin_url }] : [],
        custom: {
          department: v.department,
          seniority: v.seniority,
          return_visitor: !!v.return_visit,
          visit_duration_seconds: v.visit_duration,
        },
      }),
    });
  }

  // Task for high-intent
  if ((v.page_url || '').includes('/pricing') || v.intent_score > 70) {
    await fetch('https://api.close.com/api/v1/task/', {
      method: 'POST',
      headers: { Authorization: authHeader, 'Content-Type': 'application/json' },
      body: JSON.stringify({
        lead_id: leadId,
        text: `Hot visitor: ${v.first_name || 'Unknown'} (${v.job_title || ''}) viewed ${v.page_url}`,
        date: new Date().toISOString().slice(0, 10),
      }),
    });
  }

  return { statusCode: 200, body: 'ok' };
}

Close’s authentication model is friendly (API key as Basic auth username), which makes this far easier to deploy than the equivalent in some legacy CRMs.


Step 5: Smart Views that actually get used

A Smart View is only useful if your reps open it. Build these three, in this order.

Smart View 1: Hot visitors last 24 hours

Query:

custom.lead_source_detail:"Leadpipe Visitor ID"
AND updated > "24 hours ago"
AND custom.intent_score >= 60

Sort by intent_score descending. Pin it. This is what your AE works first thing.

Smart View 2: Return visitors

custom.lead_source_detail:"Leadpipe Visitor ID"
AND has:contact.custom.return_visitor:true

Return visitors convert 3-5x better than first-time visitors, so this one earns its keep.

Smart View 3: Target account visitors

If you keep a target account list in Close (via Opportunity status or a custom Lead tag), combine it with the Leadpipe tag:

custom.lead_source_detail:"Leadpipe Visitor ID"
AND status:"Target Account"

These deserve SDR priority, not round-robin.


What this looks like in practice

Raj, VP of Marketing at Northwind Labs, reads your “midbound” thesis post from a LinkedIn share. He does not convert. Three days later he returns and spends 4 minutes on pricing plus the enterprise page.

  1. First visit. Leadpipe identifies Raj. Webhook fires. Close creates Lead “Northwind Labs” (domain northwind.io, 240 employees, B2B SaaS), Contact “Raj Mehta” (VP Marketing, LinkedIn attached), status “Potential”, Intent score 58, last visit /blog/midbound-a-new-era-in-marketing.
  2. Return visit. Webhook fires again. Close updates the existing Lead (matched on domain), bumps Intent score to 82, flips Raj’s Return visitor to true, sets Last visit page to /pricing.
  3. A task auto-generates: “Hot visitor: Raj Mehta (VP Marketing) viewed /pricing.” Your AE’s Smart View shows Northwind at the top. They dial within the hour using the exact talking points the pricing page primes.

Midbound loops like this (and why they work) are the argument in Midbound: a new era in marketing.


Troubleshooting and edge cases

Two Leads for one company. Almost always caused by inconsistent domain formatting. Normalize before the upsert: lowercase, strip www., strip protocol. If you already have duplicates, use Close’s bulk merge to clean them up.

Contacts not linking to the Lead. The Contact API requires lead_id in the payload. If you forgot it in the Zapier mapping, Close will create orphan Contacts. Go back to the Zap step for Create Contact and re-select the Lead ID field.

GDPR EU visitors arriving without Contact data. Expected. Leadpipe defaults EU and UK visitors to company-level. Your middleware should tolerate a payload with company_domain but no email or name, and still update the Lead. See the GDPR notes post for the longer version.

Intent score field being overwritten by older data. If you enable Every Update webhooks, the last payload wins. That is usually fine because intent moves up over time, but if you want a max-intent-ever field, compute it in middleware (read current value, take max) rather than blindly patching.

Personal email leaks. Leadpipe rarely sends consumer emails on B2B sites, but your filter in Zapier should still exclude gmail.com, yahoo.com, outlook.com, icloud.com, hotmail.com. Keeps Close clean.

Task spam on return visits. If you set the task rule on page_url includes /pricing, every return visit re-creates a task. Add a condition return_visit is false, or throttle with a 24-hour cooldown in middleware.


Extending the recipe

  • Pair with Slack visitor alerts so reps see the hit in Slack at the same time Close creates the task.
  • Feed the same stream into Clay for waterfall enrichment if you want Cognism, Apollo, and PeopleDataLabs fallbacks for phone numbers before the Contact lands in Close.
  • Use the Leadpipe MCP server to let an AI SDR draft the first dial or email using Close data and Leadpipe context together.
  • If your team runs paid ads, send identified visitors into LinkedIn Ads audiences so retargeting is person-level, not domain-level.

Pricing notes

Leadpipe’s Pro plan is $147/mo for 500 identifications, which is usually enough to test this pipeline on a single product page. If you outgrow it, Growth is $299/mo for 1,500 and Scale is $599/mo for 5,000. The Close side is $29-$149/user/mo depending on the plan. The integration itself is free once you have the two accounts.

If you want the short version: $147/mo gets you person-level identification on 500 visitors with full contact data. See full pricing →