Identification and Import Are Two Different Promises
Most website visitor identification CRM pipelines treat the last step as plumbing: a visitor resolves to an email, the email becomes a contact, the contact lands in HubSpot. Technically simple. Legally, that one hop crosses a real line — under GDPR Article 14, personal data you did not collect from the person directly comes with a notice obligation, and under CPRA, moving it around can qualify as sharing.
ClickStream draws that line in the product itself. Identified visitors and CRM contacts are separate objects, and the paths between them are explicit: some are automatic, some require a named human, and every enrichment-sourced contact records who approved it and when. This post walks through what actually ships — the Promote flow, bulk promote, the auto-import controls, score-threshold automations, and the five CRM destinations behind them.
Two Kinds of Identified Visitors, Two Different Rules
ClickStream identifies visitors in two fundamentally different ways, and the CRM treats them differently on purpose:
- First-party identifications — the visitor gave your business their email or phone directly (a signup, a login, a form). These create CRM contacts automatically, through two writers: a real-time sync-identify call and an hourly sweep that catches anything the real-time path missed. The person handed you their details; recording that relationship is exactly what they expected.
- Enrichment identifications — identity attached from ClickStream's optional enrichment partner. These never auto-create contacts by default. They are structurally excluded from the first-party writers (enrichment identity lives in a separate encrypted payload, not the first-party email column), so the sweep cannot reach them even by accident.
That asymmetry is the whole design. Purchased identity becoming a CRM record is the moment your compliance obligations change — so it only happens through paths that leave a record. If you are evaluating any vendor in this space, ask how they handle exactly this boundary.
The Promote Flow: a Named Human Decides
The compliant path for an enrichment-identified visitor is Promote to contact, a button on the visitor detail view next to Send to CRM. It is deliberately narrow:
- It requires a named operator. The underlying API rejects any promotion without a real user attached — automation actors are refused outright. Viewers without CRM write access never see the button; the route denies them anyway.
- It only promotes identity the operator can already see. The button stays disabled until an email or phone has actually been revealed through ClickStream's existing access gates — the password-reauth reveal for first-party identity, or the tier-checked enrichment read for vendor identity. The promote route never decrypts anything itself.
- It states the obligation before the write. When the identity came from the enrichment partner, a compliance interstitial spells out the GDPR Article 14 / CPRA notice duty before the contact is created. When the identity is first-party, that warning correctly does not appear — the provenance is different, so the notice is different.
- Opt-outs are outcomes, not errors. If a promotion is skipped because the person opted out (
do_not_sellor a processing restriction), or no usable identity exists, the UI explains the policy result in plain language instead of surfacing a failure code.
First-party values always take precedence over vendor values when both exist — the person gave those details to your business directly, and the contact should say so.
Bulk Promote: the Same Posture at Scale
A per-profile button is legally sufficient and practically useless at 29,000 identified people. Worse, an unusable compliant path quietly pushes operators toward standing automation, which carries the heavier obligation. So bulk promote keeps the posture identical — a named human decides, on a set they chose and can see — and removes the friction.
In the People view, you select enrichment-identified rows (first-party identifications are not selectable, because they already create contacts on their own) and promote them in one action. Requests are capped at 100 records with the client paging through larger selections, and the response always reports how many were requested versus created, so a partial run is distinguishable from a full one. Partial results come back in plain language — "4 opted out", "2 had no email or phone" — because a count mismatch with no explanation reads as a bug. Every write goes through the same writer as the single-visitor path, so the consent gate, the automation-actor ban, and the tenant-boundary check apply unchanged.
Auto-Import, With the Controls in the Open
Some teams do want standing import of enrichment-identified visitors. ClickStream ships it as an explicit, org-level switch — off by default — with the guardrails visible:
- Owner or admin only. Enabling auto-import is deliberately stricter than ordinary CRM write access. The acknowledgement text you accept is stored verbatim, with who accepted it and when — an audit sees what was actually agreed, not today's copy.
- Going-forward only. Enabling stamps a start timestamp; the sweep never silently backfills history. Disabling stops imports but keeps the acknowledgement record, because the fact that someone once turned this on is exactly what an audit needs.
- A confidence floor. By default, auto-import only takes visitors resolved at the deterministic band — the same confidence-banded identity vocabulary used everywhere else in the product, not a separate threshold that could drift.
- Distinct provenance. Auto-imported contacts carry a different source label than human-promoted ones. Both are enrichment-sourced, but only one had a person look at that individual — and a controller discharging a notice duty needs to tell them apart.
- The same hard blocks. Opt-outs and processing restrictions block auto-import exactly as they block promotion, and enrichment-derived fields always land at the lowest precedence, under anything first-party.
There is also a per-site switch for the first-party side: if you want a site to stop auto-creating contacts even from direct signups, autoCreateFromFirstParty turns that writer off for that site.
Provenance You Can Export
ClickStream is not the controller of your CRM data — you are. So the settings card shows the two populations side by side ("they gave us this" versus "we bought this"), and a provenance export answers the question a regulator would actually ask: which people entered your CRM without handing you their details, and when. Enrichment-sourced contacts export separately from first-party ones, and CSV cells are prefixed to defuse spreadsheet formula injection, since contact fields can carry vendor-supplied text. It pairs naturally with the platform's scheduled compliance jobs, which handle deletion and retention on the same auditable footing.
Score-Threshold Automations
Promotion decides who becomes a contact. Automations decide what happens next. A score-threshold rule watches one of ten behavioral metrics — intent, frustration, engagement, churn risk, purchase proximity, and others from the same scoring engine that powers the Signals API — and fires when the score goes above or below a threshold you set within a time window. Rules are evaluated on a recurring sweep (about every five minutes), and prebuilt recipes cover the common cases, like tagging and notifying on high intent.
Two design choices matter here. First, every score rule is bound to exactly one site — scores are computed per site, so a rule watches one site's scoring, and the builder requires you to pick it. Second, automations can tag, notify, create tasks, move deal stages, add people to audiences, call a webhook, or send email and SMS through your own connected provider — but they cannot promote. The automation-actor ban means no rule, however configured, can turn purchased identity into a CRM record. That decision always belongs to a person.
Five Destinations, Write-Only Credentials
Contacts flow out to five destinations, configured per site in the integrations panel:
| Destination | Credentials you supply |
|---|---|
| HubSpot | Private-app access token |
| Salesforce | Instance URL, connected-app client ID and secret |
| Pipedrive | API token, company domain |
| Zoho | Client ID/secret, refresh token, data-center domains |
| Signed webhook | Any HTTPS URL you control, plus a shared secret |
Credentials are write-only: accepted when you save, encrypted at rest with AES-256-GCM under a per-site key, and never echoed back — the dashboard only ever shows that a field is configured plus a short masked hint. The webhook destination POSTs JSON batches signed with HMAC-SHA256 over the exact raw body, so your receiver can verify authenticity in a few lines:
// Verify a ClickStream CRM webhook (Node.js)
// Headers: X-ClickStream-Timestamp (Unix ms)
// X-ClickStream-Signature: sha256=<hex HMAC-SHA256(secret, rawBody)>
// Body: { "records": [ ... ] }
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifySignature(rawBody, signatureHeader, secret) {
const expected = 'sha256=' +
createHmac('sha256', secret).update(rawBody).digest('hex');
const received = Buffer.from(signatureHeader || '');
return received.length === Buffer.byteLength(expected) &&
timingSafeEqual(received, Buffer.from(expected));
}
What goes into a record is a fixed whitelist, not a field dump. Email is required — a record with no plaintext email is skipped, since CRMs dedupe on email — alongside name, company, and phone (phone only when a consented, unmasked first-party phone exists). Optional behavioral properties add visit and session counts and first/last-seen dates. Sensitive enrichment fields — income, net worth, street address, precise coordinates, raw hashed identifiers — are stripped by a record-safety scan on the way out, and visitors flagged do_not_sell or processing_restricted are excluded before a record is even built. ClickStream's own identity keys are written to your CRM only when you both opt in and accept the data-processing addendum.
What's Included at Each Plan
CRM export is cost-protected in code with per-plan monthly caps — there is no CRM-specific metering, so there is no surprise bill. Manual pushes are operator-initiated and count against a per-plan monthly record budget (Growth includes 10,000 manual records a month); auto-sync has its own caps below. See plans and pricing for the full matrix; the CRM-relevant gates:
| Plan | Manual push | Auto-sync | Auto-synced records/mo |
|---|---|---|---|
| Hobby (free) | No | No | 0 |
| Growth | Yes | No | 0 (manual only) |
| Scale | Yes | Yes | 10,000 |
| Network | Yes | Yes | 100,000 |
| Enterprise | Yes | Yes | 1,000,000 (contract) |
An inactive or past-due subscription degrades to the free tier and loses the feature — the gate fails closed rather than open.
Separately from export, CRM identity matching runs in the other direction: connect the same five providers (or upload a CSV from any CRM) and ClickStream recognizes your existing customers among your first-party visitors, matching on email and phone inside your own cryptographically isolated identity graph. It never calls a data vendor and is never billed against the enrichment add-on — it is included with paid plans (Growth and above), with its own per-plan monthly cap on synced contacts. How that graph keeps tenants separated is covered in Inside the Identity Graph.
The Bottom Line
The distance between an identified visitor and a CRM contact is where visitor identification products earn or lose trust. ClickStream's answer is structural: first-party identifications flow automatically because the person expects them to; enrichment identifications require a named human, or an org-level switch with a verbatim acknowledgement on file; opt-outs block every path; and provenance is exportable because you, not ClickStream, are the controller who has to answer for it.
A CRM full of contacts nobody remembers approving is a liability. A CRM where every enrichment-sourced contact carries who approved it and when is an asset.