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
| Requirement | Notes |
|---|---|
| Leadpipe account | Start with 500 free identifications |
| Close account | Basic plan or higher for API and webhook access |
| Close admin | To create custom fields and Smart Views |
| Zapier account | Optional, for the no-code path |
| Time | 25-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.
- Destination URL: you will generate this in Step 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.
- Fields: include everything. You will filter later.
- 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 name | Type | Purpose |
|---|---|---|
| Domain | Text | Your upsert key |
| Industry | Text | From Leadpipe |
| Employee count | Number | From Leadpipe |
| Last visit page | Text | Most recent page that triggered identification |
| Last visit at | Date | When the visit happened |
| Intent score | Number | 1-100 |
| Intent topics | Text | Comma-separated matched topics |
| Pages viewed | Text (long) | Full session |
| Lead source detail | Text | Value: “Leadpipe Visitor ID” |
Contact-level custom fields
| Field name | Type | Purpose |
|---|---|---|
| LinkedIn URL | URL | Fast verification in the timeline |
| Department | Text | Marketing, Sales, etc. |
| Seniority | Text | IC through C-level |
| Return visitor | Checkbox | Flipped when Leadpipe sends return_visit: true |
| Visit duration seconds | Number | Per-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.
- Create a new Zap. Trigger: Webhooks by Zapier, Catch Hook. Copy the URL and paste it into the Leadpipe webhook from Step 1.
- Fire the test event. Zapier shows all payload fields.
- Filter step. Suggested rule: only continue if
emaildoes not containgmail.com, yahoo.com, outlook.com, hotmail.comandvisit_durationis greater than 20. - Formatter step (important). Lowercase the
company_domainand stripwww.. Close will happily create two Leads foracme.ioandwww.acme.ioif you do not. - 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”). - 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. - Action: Close, Update Lead. Patch Last visit page, Last visit at, Intent score, Intent topics, Pages viewed.
- Conditional action. If
page_urlcontains/pricingorintent_scoreis 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.
- 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. - 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. - 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 →