Full funnel tracking
The feature that makes everything else work: following one person from the first time they saw your brand to the day they churned, across devices, sessions, browsers, and payment events.
This page explains the identity model — the mechanism by which an anonymous pageview becomes
a named customer. Understanding it helps when you're debugging "why isn't this user showing up
in my funnel?" or deciding when to call identify.
The three identities
FunnelFizz stitches three kinds of identifiers together:
- Visitor ID (
_fn_vidcookie) — assigned on first pageview. Permanent per browser. UUID. - User ID — your own internal ID for the user (however your app names them). Assigned
when you call
funnelfizz('identify', { userId }). - Stripe customer ID —
cus_.... Assigned by Stripe when checkout happens.
The goal is to link these three together so that a session that started anonymous on mobile can be tied to the desktop purchase three weeks later.
The stitch
Day 1: anonymous visitor lands on your site.
→ _fn_vid cookie = "vid_abc123" (new UUID)
→ _fn_sid session = "sess_def456"
→ pageview event recorded, attributed to vid_abc123
Day 2: same visitor, signs up for a trial.
→ app JS calls: funnelfizz('identify', { userId: 'user_789' })
→ VisitorIdentity row written: vid_abc123 → user_789
→ all prior events re-attributed to user_789
Day 3: user checks out on Stripe.
→ Stripe fires checkout.session.completed webhook
→ FunnelFizz matches customer.email to user_789
→ writes journey event: stage=CUSTOMER, visitor=vid_abc123, user=user_789
→ stripe_customer_id = "cus_xyz" stored on the profile
Day 30: same user, different device (new browser, no cookie).
→ anonymous pageview → _fn_vid cookie = "vid_new999"
→ pageview recorded attributed to vid_new999
→ NO stitch yet — FunnelFizz doesn't know this is user_789
Day 30 later: user logs in on the new device.
→ app JS calls: funnelfizz('identify', { userId: 'user_789' })
→ VisitorIdentity row written: vid_new999 → user_789
→ BOTH vid_abc123 and vid_new999 now point to user_789
→ all their events, on both devices, roll up to the same profile
Keys and cookies
| Name | Storage | Lifetime | Purpose |
|---|---|---|---|
_fn_vid | Cookie + localStorage | 365 days | Permanent visitor ID |
_fn_sid | sessionStorage | Tab lifetime | Session ID for grouping pageviews |
Neither cookie is shared across domains. If your product is split across
marketing.example.com and app.example.com, enable cross-subdomain cookies by setting
cookieDomain: '.example.com' when you install tracking (see
Features → Providers for multi-domain setups).
The identify call
The single most important call in your codebase:
funnelfizz('identify', {
userId: 'user_789', // your internal user ID — required
email: 'sam@example.com', // optional but strongly recommended
createdAt: '2026-04-22T...', // optional — helps with cohort analysis
plan: 'pro', // optional custom trait
});
Where to call it:
- Right after signup (immediately — even before the first trial starts).
- Every page load after login (idempotent, safe to call repeatedly).
- After any auth state change (on every re-auth, OAuth callback, etc.).
What not to pass:
- Passwords, tokens, credit card info — anything sensitive.
- PII you haven't gotten consent to process (in GDPR jurisdictions).
Cross-device identity
When the same userId is seen on two different _fn_vid cookies, FunnelFizz merges them —
all events roll up to the same user profile, regardless of which device fired them. This is
automatic; no extra config.
The reverse isn't true: we don't guess that two anonymous visitors on different devices are the
same person. If you never call identify, anonymous sessions stay separate. This is
intentional — cross-device guessing without identity signals is how creepy tracking happens.
Email handoff (from email campaigns)
When FunnelFizz sends an email on your behalf, it embeds a signed profile token into every tracked link:
https://your-site.com/pricing?fn_ph=<signed-token>
When the recipient clicks, the tracking script reads the token on first event, verifies the signature server-side, and stitches the recipient's profile to the landing visitor cookie — so you can track "this trial user clicked our day-5 email and visited pricing," even if they opened the email on a totally new device.
The token is stripped from the URL via history.replaceState immediately so it doesn't leak
to downstream analytics or referrer headers.
Journey events
Every stage entry and conversion writes a journey event:
{
"funnelId": "funnel_abc",
"visitorId": "vid_123",
"userId": "user_789",
"stageType": "TRIAL",
"eventType": "entered",
"fromStage": "CONSIDERATION",
"metadata": { "method": "stripe_trial", "productId": "prod_xyz" },
"createdAt": "2026-04-22T12:34:56Z"
}
Unique constraints per (funnel, visitor, stage, eventType) guarantee that a visitor can't enter
the same stage twice in the same funnel. Churn events are the exception — a customer can churn
only once per funnel (unique on funnel × visitor × stageType = CUSTOMER × eventType = "churned").
Full journey view
On any profile's detail page, you'll see a chronological timeline:
2026-04-01 12:04 pageview / Reddit
2026-04-01 12:05 pageview /pricing
2026-04-02 09:22 identify user_789 sam@example.com
2026-04-02 09:23 signup (custom event)
2026-04-05 14:10 stripe_checkout cus_xyz → prod_pro $9/mo trial
2026-04-12 22:30 subscription_active paid
2026-05-12 22:30 subscription_canceled churned
This is the raw source of truth. Dashboard metrics are aggregations; the journey is the trace.
Privacy
- No third-party tracking. FunnelFizz doesn't sell data to ad networks.
navigator.doNotTrackis respected — if the visitor has DNT set, no events are sent.- GDPR / CCPA: you're responsible for consent. If you're using a consent management platform (CMP), defer loading the FunnelFizz snippet until the user grants "analytics" consent.
- All tokens in email links are short-lived and single-scoped.
- Profile tokens in the URL are HMAC-signed and verified server-side; an attacker can't guess another user's token.
See funnelfizz.com/privacy for the full policy.
Next: browse the Features section for the full details on each feature.