Skip to main content

Lead Intelligence (B2B Enrichment)

Audience: Sales · Management · Support · Engineering · QA · Where in app: Settings → Integrations; results shown on Live Sessions, Recorded Sessions, Leads → B2B Leads, Person Detail · Plan availability: Pro and up (admin-gated; consumes a separate query budget) — verify exact tier

Lead Intelligence turns anonymous website traffic into identified companies (and sometimes contacts) using reverse-IP and contact-enrichment providers. When an unknown visitor from "Acme Corp" lands without filling anything out, this is what surfaces "Acme Corp" instead of "Guest User." It's admin-gated, draws down a per-company budget, and is best-guess by nature — never identity verification.

What it does

  • Resolves a visitor's IP/network to a company (name, domain, industry, employee count, revenue band).
  • Optionally resolves contact-level suggestions (RB2B) and email/domain → person enrichment (Clearbit).
  • Feeds the resolved company onto the session, the B2B Leads view, and the industry_match lead-scoring signal.
  • Caches results to conserve budget and optionally syncs enriched contacts to a connected CRM.

How it works

Providers

The active session-create enrichment chain is Snitcher → Apollo; the others are legacy paths still in the codebase but no longer live-fetching on session create.

ProviderStatusToggle / budget
SnitcherActive. Reverse IP → company (+ visitor type: business/isp/hosting)companies.snitcher_enabled + snitcher_allowed daily cap
ApolloActive. Company domain (from Snitcher) → person-level enrichment. No separate quota — only runs after Snitcher resolves a domain, so snitcher_allowed effectively caps itcompanies.apollo_enabled
HappierLeadsLegacy webhook path still live; its CRM push job is disabled globallycompanies.happier_leads_config
RB2BLegacy; cache models remaincompanies.rb2b_enabled + rb2b_allowed
ClearbitLive API call disabled (commented out); cache-read path onlycompanies.allow_clearbit

When it fires

Triggered in UserSessionController.create when all hold (order matters — this is the quota-conservation logic):

  1. The identity graph resolved nothing first — widget-id backfill and device-signature linking run before enrichment (_backfillPersonFromPriorVisit, personResolver.wouldClaimDeviceinternally_resolved). Returning identified visitors never spend quota.
  2. suspected_bot !== truebots never spend quota.
  3. A user_ip exists — when the client's IP lookup is ad-blocked, the server fills it from x-forwarded-for / x-real-ip.
  4. The provider is enabled and the daily quota has headroom (see below).
  5. If the tenant has consent gating enabled with the identification category, the visitor granted it (tracking_consent); a mid-session grant runs enrichment forward-only via POST /user-session/:id/grant-tracking.

It runs async — it never blocks the session. The result populates UserSessionModel enrichment fields and drives the industry_match lead-scoring signal.

Daily quota (Snitcher)

UserSessionController.getSnitcherData: daily cap company.snitcher_allowed, counted in the company-local timezone (moment.tz(company.timezone || 'UTC')). The day window 06:00–24:00 gets the full cap; the night window 00:00–06:00 local spends only yesterday's leftover (blocked once nightCount >= dailyCap - yesterdayCount). The 06:00 boundary is hardcoded.

Caching

Results are cached per provider (SnitcherSessionModel, ApolloRecordModel, plus legacy Rb2bSessionModel / ClearbitSessionModel / HappierLeadSessionModel). Re-queries within the TTL hit the cache and don't draw down quota. Apollo error-cache poisoning fix: Apollo responses with status ≥ 400 (and failed people/match reveals) are never cached, so a transient API error can't permanently blank a domain; POST /admin/identity/maintenance/cleanup-apollo-records purges already-poisoned ApolloRecordModel rows so those domains retry.

CRM sync

The enrichment-guess CRM push job (LeadContactSyncJob, HappierLeads contacts → GHL/HubSpot) is disabled globally — its scheduler registration is commented out in SchedulerService.js. Enrichment guesses stay inside Knock Knock; only first-party identities flow to the CRM. See CRM Integration.

What gets enriched

InputOutput
IP → Company (reverse IP)Company name, domain, industry, employee count, revenue band
Email → Person (Clearbit)Title, role, social profiles
Domain → CompanyIndustry tags, technographics

Configuration & options

SettingDefaultEffect
companies.snitcher_enabled / snitcher_allowedoff / 20Enable Snitcher + its daily cap (counted in company-local timezone)
companies.apollo_enabledoffEnable Apollo person enrichment (capped implicitly by Snitcher)
companies.consent_gating (identification)offWhen enabled, Snitcher/Apollo require visitor consent — see Consent Gating
companies.lead_intelligence_allowed40Monthly overall lookup budget
SubscriptionModel.remaining_lead_intelligenceRolling balance for the current period
companies.rb2b_enabled / rb2b_allowedoff / 20Legacy RB2B toggle + budget
companies.happier_leads_configLegacy HappierLeads enablement/config (webhook path)
companies.allow_clearbitoffLegacy Clearbit (cache-read only; live fetch disabled)
IP ignore-listSkip cloud/VPN/own-office IPs

When the budget hits zero, lookups pause until reset (subscription anniversary or manual top-up). Each provider has its own cost model — lead_intelligence_allowed is roughly that many lookups, but high-quality matches typically cost more than cache hits.

Where to see enrichment: Live Sessions (company name next to anonymous visitors), Recorded Sessions (same), Leads → B2B Leads (enriched-only view), and Identity → PersonProfile (when an enriched company ties to a Person). Leads → Export produces a CSV with all enriched fields.

Behaviors & edge cases

  • Company match is a best-guess, not identity verification. Never treat "company X visited" as "a real person at company X visited" — it's the IP they came from. In the identity graph, enrichment can never mint verified and never overwrites a self-provided name — it only fills empty fields.
  • Session naming tiers. The UI names a session self-provided → suspected → Apollo person → Snitcher company (only type === 'business') → Snitcher domain with a ⚠ prefix for non-business (isp/hosting) matches (getSessionName, frontend/src/common/helpers/session.js).
  • Smaller companies share IPs with parent companies or ISPs, so matches can be off.
  • B2C / residential IPs usually can't be enriched — most providers only resolve corporate networks.
  • Cloud/VPN IPs are typically unresolvable (and on the ignore-list).
  • "Why am I not seeing companies?" — check the per-tenant provider toggles, the remaining lead_intelligence_allowed budget, and whether the visitor's IP is cloud/VPN/residential.
  • Budgets are per company; agency-managed companies inherit from their package.
  • Cache TTL means a repeat visitor from the same network won't re-spend budget — but also won't refresh stale company data within the TTL.

Plan & limits

  • Lead Intelligence is admin-gated and plan-gated (Pro and up per the KB — verify exact tier), and consumes the lead_intelligence_allowed budget (default 40/month) plus per-provider budgets (rb2b_allowed, snitcher_allowed, default 20 each).
  • Budget exhaustion pauses lookups until the period resets or a manual top-up. Higher tiers / add-ons raise the budgets — verify per-tier allotments.

Technical implementation

  • Services: backendUserSessionController.getSnitcherData / getApolloData (the active chain, including quota + consent gates); legacy HappierLeadsService, Rb2bService, Clearbit paths; CrmEnrichmentQueueService for async enrichment; IdentityGraphService consumes enriched data.
  • Models / cache: SnitcherSessionModel, ApolloRecordModel, legacy Rb2bSessionModel / ClearbitSessionModel / HappierLeadSessionModel; results land on UserSessionModel enrichment fields and can fill PersonProfileModel (fill-empty only).
  • Budgets: companies.lead_intelligence_allowed, rb2b_allowed, snitcher_allowed; SubscriptionModel.remaining_lead_intelligence tracks the rolling balance.
  • Nox tools: knox-stats, knox-aggregate, knox-person-lookup — budget remaining, top enriched companies, whether/how a visitor was enriched, cost-per-conversion when enrichment was the lead source.
  • Subsystems: Identity Graph (enrichment is a resolution signal), CRM Integration.