Privacy & compliance
ClickStream is built for operators who want to own their visitor data without inheriting a vendor's consent-posture problems. The SDK ships strict-by-default: no identity resolution, no behavioral trackers, no fingerprinting, no replay — until the visitor's consent state says otherwise.
Consent model
Every event the SDK emits is gated by a consent state stored in the _cs_consent first-party cookie + local-storage slot. The consent state is a tri-state per category:
| Category | Default | What it gates |
|---|---|---|
analytics | granted or banner_default depending on your site's compliance preset | pageview, click, scroll, session tracking |
identity | denied until explicit grantConsent('identity') | HEM / phone / customer-id / social-id capture + transmission |
marketing | denied until explicit grantConsent('marketing') | click-id attribution (gclid, fbclid, etc.), UTM capture, replay, behavioral trackers |
Revoking consent clears every identity slot from storage + cookies, pauses the affected trackers, and emits a _consent_transition event so the server can scrub downstream state.
Compliance presets
Set the preset at install time via data-compliance or the compliance config option:
<script
src="https://t.example.com/sdk.js"
data-key="cs_live_..."
data-compliance="gdpr_strict"
async
></script>
new IdentityTracker({ apiKey, endpoint, compliance: 'gdpr_strict' });
| Preset | Consent mode | Banner on load | Scrub level | Identity / fingerprint default | Retention |
|---|---|---|---|---|---|
standard | opt_out | No | standard | allowed when visitor identifies | indefinite (per plan) |
gdpr_strict | opt_in | Yes | aggressive | denied until marketing consent | 13 months |
ccpa | opt_out | No (link in footer sufficient) | standard | allowed; honors DNT + GPC | 12 months |
hipaa | opt_in | Yes | aggressive + field encryption | denied | contract-specific |
cpra | opt_out | No | aggressive | honors GPC auto-opt-out | 12 months |
The preset sets server-side defaults via ClientConfig.complianceProfile, which the collector uses to reject any event that carries fields the preset would have stripped — defense-in-depth against an old SDK bundle being cached on a visitor's device.
Banner + CMP detection
When showBannerOnLoad: true, the SDK renders the built-in banner on first visit with Accept-All / Reject-All / Customize controls. You can style it to match your site via consentBannerConfig or bring your own banner entirely:
import { hasConsent, grantConsent, revokeConsent } from '@clickstream/sdk';
hasConsent('analytics'); // boolean
grantConsent(['analytics', 'identity']);
revokeConsent('marketing');
The SDK also auto-detects existing CMPs (OneTrust, Cookiebot, Didomi, TrustArc, Osano, Sourcepoint) via their IAB TCF v2 API and mirrors the consent decision. You don't have to double-banner your visitors.
What's scrubbed at which level
standard scrub
- Query strings on
page.urlmatching common PII patterns (token=,email=,password=,access_token=,ssn=) are dropped. - Click element text longer than 128 characters is truncated.
- Form field values are NOT captured unless Universal Form-Fill Capture is explicitly enabled per site.
aggressive scrub
All standard rules plus:
document.referreris reduced to origin + path (query + fragment stripped).- Any field matching the SDK's broad PII heuristics (name, DOB, SSN, license, CC, health terms) is skipped even under form capture.
- Click text is truncated to 64 characters.
Raw PII — encrypted at rest, reveal-gated
When a site enables raw-value capture (email pre-encrypt on the server, form fills, IP addresses), values are AES-256-GCM encrypted with a unique key per site before they hit D1. Raw plaintext is never persisted anywhere.
Reveal requires all of:
- Dashboard operator with
decrypt:readpermission. - Password re-authentication within the last 5 minutes (
/decryptre-auth gate). - A recorded audit row (
audit_log) that captures operator id + IP + target visitor + timestamp + reason text. - Rate-limit allowance (10 reveals per operator per 5 minutes).
The audit log is append-only, retained 7 years, and surfaced to site admins on the Security → Reveal Audit tab. No one at ClickStream can reveal raw values on your behalf; the keys live under per-site HSM-backed KMS in your Cloudflare account.
Visitor rights — DSAR / export / deletion
Every dashboard site admin can:
- Export all data for a visitor — CSV + JSON bundle of every event, identity signal, form submission, and encrypted-field reveal associated with the
_cs_uidor any of its linked identifiers. - Delete a visitor — soft-delete the person record, null all hashed identifiers, scrub raw encrypted blobs, tombstone the cookie. Retained: aggregate counts (pageviews-per-day) because they're already non-personal.
- Honor DNT / GPC — the SDK auto-respects
navigator.doNotTrack === '1'and Global Privacy Control (navigator.globalPrivacyControl === true) when compliance preset isccpaorcpra.
Use tracker.revokeIdentity() + the dashboard delete action for a full visitor scrub.
Server-side compliance enforcement
The SDK already strips fields based on the compliance profile before transmission, but the collector strips them again as defense-in-depth. A cached old SDK bundle on a visitor's device can't smuggle fields past the collector — the server's ClientConfig.complianceProfile rules run on every event regardless of SDK version.
Regional considerations
- GDPR / UK GDPR — use
gdpr_strict. Consent banner on load with explicit opt-in. Processing under "legitimate interest" is deliberately NOT supported for marketing / identity categories. - CCPA / CPRA — use
ccpaorcpra. No banner required; a "Your Privacy Choices" link in your footer satisfies notice-at-collection. GPC honored server-side. - HIPAA (US healthcare) — use
hipaa. Enables field-level encryption for any capture. Contact support before launch — a signed BAA is required before you can go live with PHI processing. The SDK supports HIPAA-adjacent use cases but we do not sell to covered entities out of the self-serve flow. - PIPEDA (Canada) —
standard+ explicit consent flow on first visit meets PIPEDA baseline. - LGPD (Brazil) — use
gdpr_strict— LGPD's consent requirements are essentially a GDPR subset.
Data residency
- Primary storage — Cloudflare's global network (Analytics Engine + D1 + KV). Logical region assignment follows Cloudflare's data-at-rest residency program.
- EU-only residency — available on Custom tier with a signed DPA specifying
cf-region: eu. Enforced at the KV + D1 binding level; thewrangler.tomlfor your dedicated tenant sets the region constraint. - US-only residency — same story,
cf-region: us. - Cross-border transfers — we do not share hashed or raw PII with third-party identity graphs (LiveRamp / TTD / ID5 / UID2 public) unless the site explicitly opts in on the Identity tab. Default is first-party only.
DPA + sub-processor list
A Data Processing Agreement is available on request for Builder+ tiers; Scale+ customers get a DPA appended to the MSA on provisioning. Current sub-processors:
- Cloudflare — ingestion, storage, compute, DNS.
- Stripe — billing + subscription metering. Does NOT receive any event data.
- AWS — roadmap for Nitro Enclave UID2 private-operator mode. Currently not in the processing path.
The sub-processor list is published at einstein.clickstream.com/legal/sub-processors — we update it 30 days before adding any new sub-processor.
See also
- Security — encryption primitives + per-tenant isolation
- Identity resolution — what gets hashed where
- Event schema — consent transitions —
_consent_transitionevent - First-party tracking (required) — tenant-scoped DNS + SSL provisioning