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_matchlead-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.
| Provider | Status | Toggle / budget |
|---|---|---|
| Snitcher | Active. Reverse IP → company (+ visitor type: business/isp/hosting) | companies.snitcher_enabled + snitcher_allowed daily cap |
| Apollo | Active. Company domain (from Snitcher) → person-level enrichment. No separate quota — only runs after Snitcher resolves a domain, so snitcher_allowed effectively caps it | companies.apollo_enabled |
| HappierLeads | Legacy webhook path still live; its CRM push job is disabled globally | companies.happier_leads_config |
| RB2B | Legacy; cache models remain | companies.rb2b_enabled + rb2b_allowed |
| Clearbit | Live API call disabled (commented out); cache-read path only | companies.allow_clearbit |
When it fires
Triggered in UserSessionController.create when all hold (order matters — this is the quota-conservation logic):
- The identity graph resolved nothing first — widget-id backfill and device-signature linking run before enrichment (
_backfillPersonFromPriorVisit,personResolver.wouldClaimDevice→internally_resolved). Returning identified visitors never spend quota. suspected_bot !== true— bots never spend quota.- A
user_ipexists — when the client's IP lookup is ad-blocked, the server fills it fromx-forwarded-for/x-real-ip. - The provider is enabled and the daily quota has headroom (see below).
- 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 viaPOST /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
| Input | Output |
|---|---|
| IP → Company (reverse IP) | Company name, domain, industry, employee count, revenue band |
| Email → Person (Clearbit) | Title, role, social profiles |
| Domain → Company | Industry tags, technographics |
Configuration & options
| Setting | Default | Effect |
|---|---|---|
companies.snitcher_enabled / snitcher_allowed | off / 20 | Enable Snitcher + its daily cap (counted in company-local timezone) |
companies.apollo_enabled | off | Enable Apollo person enrichment (capped implicitly by Snitcher) |
companies.consent_gating (identification) | off | When enabled, Snitcher/Apollo require visitor consent — see Consent Gating |
companies.lead_intelligence_allowed | 40 | Monthly overall lookup budget |
SubscriptionModel.remaining_lead_intelligence | — | Rolling balance for the current period |
companies.rb2b_enabled / rb2b_allowed | off / 20 | Legacy RB2B toggle + budget |
companies.happier_leads_config | — | Legacy HappierLeads enablement/config (webhook path) |
companies.allow_clearbit | off | Legacy Clearbit (cache-read only; live fetch disabled) |
| IP ignore-list | — | Skip 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
verifiedand 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_allowedbudget, 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_allowedbudget (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:
backend—UserSessionController.getSnitcherData/getApolloData(the active chain, including quota + consent gates); legacyHappierLeadsService,Rb2bService, Clearbit paths;CrmEnrichmentQueueServicefor async enrichment;IdentityGraphServiceconsumes enriched data. - Models / cache:
SnitcherSessionModel,ApolloRecordModel, legacyRb2bSessionModel/ClearbitSessionModel/HappierLeadSessionModel; results land onUserSessionModelenrichment fields and can fillPersonProfileModel(fill-empty only). - Budgets:
companies.lead_intelligence_allowed,rb2b_allowed,snitcher_allowed;SubscriptionModel.remaining_lead_intelligencetracks 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.
Related
- Leads — Overview — B2B enrichment as a lead source
- Lead Scoring —
industry_matchis driven by enrichment - Live Sessions — anonymous → company resolution on the live list
- Identity Graph · CRM Integration