Engineering

B2B Visitor Identification Without a Form Fill

What a hashed IP, a precision-7 geohash, and business hours can honestly tell you about buying groups — before anyone types an email.

July 2026 • 8 min read

The Form Fill Is a Lagging Indicator

In B2B, the demo request arrives at the end of the story. By the time someone types their work email into your form, a group of colleagues has usually already read your pricing page, compared you to alternatives, and argued about you in a meeting you'll never see. Most B2B visitor identification tools respond to this by looking your visitors up in a third-party IP-to-company database — someone else's data, of unknown age, matched by methods you can't inspect.

ClickStream takes a different approach: it clusters the anonymous visitors you already have, inside your own tenant of the identity graph, using only your own first-party traffic. Two shipped features do this work — household clustering (migration 0008 of the identity graph schema) and workplace clustering (migration 0009). This essay walks through exactly how both work, including the parts where the system deliberately refuses to draw conclusions.

Household Clustering: One Hashed IP, One Tenant

A household cluster is the simplest environmental grouping in the graph: visitors whose events arrive from the same network egress. The graph never stores the IP address itself — the cluster is keyed on a hash:

-- migrations/0008_household_clustering_edge_ttl.sql (abridged)
CREATE TABLE IF NOT EXISTS household_nodes (
  id TEXT PRIMARY KEY,                     -- UUID
  ip_hash TEXT NOT NULL,                   -- HMAC or SHA-256 hashed IP
  tenant_id TEXT NOT NULL,                 -- Tenant scope
  connection_type TEXT DEFAULT 'unknown',  -- residential, business, mobile, hosting
  member_count INTEGER DEFAULT 0,
  confidence REAL DEFAULT 0.6,             -- Household confidence (decays with age)
  expires_at INTEGER,                      -- 30-day TTL from last activity
  UNIQUE(ip_hash, tenant_id)
);

Three properties of this table are load-bearing:

The exclusion list is one line in the graph service, and it reads exactly like you'd hope:

// apps/identity-graph/src/services/graph.ts
const EXCLUDED_CONNECTION_TYPES = new Set(['hosting', 'vpn', 'proxy', 'tor']);

For B2B2C products — where a business sells through to consumers — the household is the natural unit of decision-making: the family plan researched on two phones and a laptop, the insurance quote one partner starts and the other finishes. Household clustering lets you see that these sessions belong to one decision, without ever claiming they belong to one person.

Workplace Clustering: Geohash, ASN, and Business Hours

Workplaces need a stricter definition than households, because office buildings share networks with the café downstairs and coworking floors churn tenants. The workplace cluster is keyed on three things at once:

-- migrations/0009_geo_location_workplace_clustering.sql (abridged)
CREATE TABLE IF NOT EXISTS workplace_nodes (
  id TEXT PRIMARY KEY,
  geohash TEXT NOT NULL,            -- Precision 7 for tight clustering
  tenant_id TEXT NOT NULL,
  asn INTEGER,
  as_organization TEXT,
  member_count INTEGER DEFAULT 0,
  confidence REAL DEFAULT 0.4,
  expires_at INTEGER,               -- 60-day TTL (longer than household)
  UNIQUE(geohash, tenant_id, asn)
);

Unpacking the key:

Confidence is tuned even more conservatively than for households: a member link starts at 0.3 and grows by just 0.03 per business-hours observation, capped at 0.85. The workplace node itself starts at 0.4 and gains 0.05 per distinct member, with the same 0.85 ceiling. It takes sustained, repeated, weekday-daytime activity from multiple people before the graph treats a location as a workplace with any conviction — and it never becomes fully convinced.

What a Cluster Buys You Before the Form Fill

Now the B2B payoff. When several distinct visitors cluster in the same precision-7 cell on the same ASN, during business hours, across multiple weeks — that is what a buying committee looks like in first-party data. You can see that a group at one location is reading your integration docs, returning to pricing, and comparing plans, while each member is still individually anonymous. Combined with per-visitor intent and frustration scores from the Signals API, that turns "some traffic from somewhere" into "a team somewhere is evaluating us, and they're accelerating."

Two honest limits, stated plainly:

The cluster becomes dramatically more useful the moment any one member stops being anonymous. When one person on the team signs up, logs in via a social login, or fills the form, their person node gains a deterministic anchor — and every other member of that workplace cluster is now context around a named account. That promotion path, including its consent gates, is its own essay: from anonymous visitor to CRM contact, the compliant path.

The Guardrail: Environmental Signals Never Merge People

Here is the part that makes the feature trustworthy rather than creepy. Sharing a household or a workplace is evidence about an environment, not about a person — and the graph enforces that distinction structurally. The merge logic that folds two person nodes into one accepts only deterministic keys: hashed emails and phone numbers, tenant-scoped customer and account IDs, and authenticated social-login subjects. A shared mobile ad ID can trigger a merge only with independent corroboration, because family members share tablets. Household membership, IP hashes, and geohashes are not on the list at all. They cannot merge people, no matter how strong the co-occurrence looks.

Signal Confidence Weight Can It Merge Two People?
Hashed email (hem / hmac_hem) 0.30 Yes — deterministic anchor
Social-login subject (Google, Facebook, Apple, LinkedIn) 0.25 Yes — deterministic anchor
Customer / account ID 0.20 Yes — tenant-scoped deterministic anchor
Mobile ad ID (MAID) 0.10 Only with independent corroboration
Device fingerprint 0.08 No — corroborating evidence only
Postal code 0.06 Never
Geohash 0.05 Never
Metro code 0.04 Never
Timezone 0.03 Never

The weights tell the same story from another angle: a geohash contributes 0.05 to a person's resolution confidence where a hashed email contributes 0.30. Environmental signals season the profile; they never define it. This is the same philosophy behind the graph's confidence bands — the strength of a claim is pinned to the strength of its evidence.

There's one subtle case worth spelling out. When two nodes look like merge candidates because they're geographically close, the graph demands corroboration that is independent of geography — specifically a shared high-confidence device fingerprint. Shared postal codes, timezones, and metro codes are explicitly rejected as corroboration, because the reasoning would be circular: the candidate pair exists precisely because the two nodes are near each other. The source comment says it plainly — accepting shared geography as its own proof "merges strangers who happen to share a metro." Colleagues in one office stay separate people in the graph until real person-level evidence says otherwise.

Per-Tenant Isolation: Your Clusters Are Yours

Look back at the two unique constraints: UNIQUE(ip_hash, tenant_id) for households and UNIQUE(geohash, tenant_id, asn) for workplaces. The tenant ID is inside the key. The same office building visiting two different ClickStream customers produces two entirely unrelated workplace nodes; membership rows carry a tenant ID too. There is no pooled cross-customer household or workplace database, and your competitors' traffic teaches your graph nothing — in either direction.

That's a real trade-off, and it's the honest one. Pooled B2B intent networks get broader coverage by commingling everyone's visitors, and you inherit whatever data-quality and consent decisions the pool made. If you're weighing bought account data against first-party clustering, we wrote up how to evaluate audience data vendors without getting burned.

Clusters That Expire

Environmental facts go stale faster than identities. People change jobs, leases end, and that café that briefly looked workplace-ish on weekday afternoons stops mattering. So every probabilistic edge in the graph carries a TTL and gets pruned; only deterministic person signals — hashed emails and phones — persist without expiry:

Edge Type TTL
Session ID 7 days
Visitor ID / device fingerprint / geohash 14 days
Household / mobile ad ID 30 days
Workplace 60 days
Ad-platform click ID 90 days
Hashed email / hashed phone (deterministic) No expiry

Workplaces get twice the household TTL because, as the source comment notes, workplaces are more stable — but both decay. A cluster that isn't refreshed by real visits disappears. An identity system that only ever adds edges eventually believes everything; this one is built to forget.

The Bottom Line

B2B visitor identification doesn't have to mean buying a lookup table of other people's guesses. ClickStream's household and workplace clustering builds account-level context from your own first-party traffic, under constraints you can read in the schema:

If you want to see what your own traffic clusters into, installation takes a script tag — and the plans page shows what each tier includes. The buying committee is already on your site. You don't need their form fill to notice them — and you don't need to violate anyone's privacy to serve them better.

See the Buying Groups Already on Your Site

Household and workplace clustering runs inside your own tenant, on your own first-party traffic — no bought lookup tables, no cross-customer pooling.

Start free