API docs / Webhooks
Inbound webhooks
Three ways to send a lead into CleanLeads. All three use the same API key, write to the same lists and contacts, and can produce a first-touch intro. None of them invent a person from a cookie graph.
Authentication
Create or copy your key in Settings → Security. Send it as X-API-Key or Authorization: Bearer. Events show up in Admin → Inbound webhooks.
1. Form webhook
Put this behind HighLevel, Typeform, Tally, or any form that can POST JSON. Required: email, company, and a name.
POST https://www.cleanleadsai.com/api/advisory/leads/webhook
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"fullName": "Alex Morgan",
"email": "alex@acme.com",
"company": "Acme HVAC",
"roleTitle": "Owner",
"phone": "+15125550123",
"clid": "optional-clid-from-pixel",
"leadSource": "facebook",
"formId": "ai-advisory",
"utm": { "utm_source": "facebook", "utm_campaign": "spring" }
}CleanLeads confirms the person (Prospeo, then PDL if enabled), enriches the company, and writes an intro. Lists look like Webhook - Facebook - Ai Advisory. Dedupes on email + company (30 days), externalId, or clid.
2. Inbound call webhook
For Twilio, HighLevel voice, or any inbound-call system. Required: phone.
POST https://www.cleanleadsai.com/api/advisory/leads/call-webhook
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"phone": "+12024561111",
"leadSource": "twilio",
"externalId": "CAxxxxxxxx"
}Waterfall is cheap-first: validate the number, Google business match, US public-records name, Apollo, then Harvest only if LinkedIn is still missing. Then company website and intro. Dedupes on phone (30 days) or externalId (use the carrier CallSid).
3. Website visit pixel
Leadfeeder-style company identification, not person-level anonymous deanonymization. The Cloudflare Worker reads the visitor IP and ASN organization at the edge, then posts to CleanLeads. Residential ISPs are ignored. Business networks become a company record, then website + intro.
Install the script
Paste this before </body> on every page you want to measure. Use the same API key as the other webhooks.
<script src="https://trk.cleanleadsai.com/pixel.js" data-pixel="px_YOUR_PIXEL_ID" async ></script>
Admins copy a ready-made snippet from Settings → Site pixel or Admin → Site pixel. Each account gets a signed px_ id — do not put the API key in public HTML. Until DNS for trk.cleanleadsai.comis live, the Worker is also at https://cleanleads-pixel.chris-f7c.workers.dev/pixel.js.
Optional attributes
data-require-consent="true"— only fires whenwindow.cleanleadsConsent === true. Use this with your cookie banner.data-clid-param="clid"— query parameter that becomes the first-party identity cookie (defaultclid).
How a pageview is handled
- The script no-ops if Global Privacy Control is on, or if consent is required and not granted.
- It stores
_cl_sid(session),_cl_clid, and optional_cl_email/_cl_company(180 days) as first-party cookies. - It beacons page, host, title, referrer, UTM, session, clid, and any first-party identity it already has (email, name, phone, company, timezone, language).
- The Worker adds IP, city, region, country, and ASN organization from Cloudflare.
- CleanLeads resolves a company (ASN, optional IPinfo, then our website lookup). Repeat visits from the same session or company within 24 hours update the page history and do not re-spend enrichment.
Server-to-server (no script)
POST https://www.cleanleadsai.com/api/advisory/leads/site-webhook
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"page": "https://yoursite.com/pricing",
"host": "yoursite.com",
"sessionId": "optional",
"clid": "optional-contact-id",
"email": "optional-first-party@acme.com",
"company": "optional company from form or thank-you URL",
"ip": "203.0.113.10",
"asOrganization": "Acme HVAC LLC",
"city": "Austin",
"region": "Texas",
"country": "US",
"utm": { "utm_source": "linkedin" }
}CLID stitching
CLID is how a known person and an anonymous session become the same contact. Put the CleanLeads contact id (or any id you already stored) on outbound links:
https://yoursite.com/pricing?clid=CONTACT_UUID
The pixel copies that value into _cl_clid. Later pageviews and form posts that include the same clidattach to that contact instead of opening a new one. This is first-party identity — they clicked your link or submitted a form — not a broker cookie.
Home broadband visits stay labeled as local network. To illuminate those visitors into a business, pass first-party fields the site already has: work email, company, name, or phone on the URL, or call identify after a HighLevel form:
window.cleanleadsIdentify({
email: "{{contact.email}}",
clid: "{{contact.id}}",
firstName: "{{contact.first_name}}",
lastName: "{{contact.last_name}}",
company: "{{contact.company_name}}",
phone: "{{contact.phone}}"
});A work email domain (not Gmail/Yahoo) becomes the company website hint and we run the same company card lookup. Personal emails and empty HighLevel merge tags are ignored. We do not fingerprint devices or read third-party cookies.
Consent and privacy
- US default: the pixel may run as first-party analytics. Honor GPC automatically.
- EU/UK/CA: set
data-require-consent="true"and flipwindow.cleanleadsConsent = trueonly after the visitor accepts analytics/marketing. - We identify companies on business networks. Home ISPs stay labeled as local broadband unless the visitor already gave first-party identity (clid, work email, company). We do not sell a name, email, or postal address from an anonymous cookie.
- Disclose website analytics and company-level identification in your privacy policy. Sign a processor agreement before using the pixel on a client site.
Need endpoint-level tool docs? Open the API reference.