Skip to main content

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:

  1. Visitor ID (_fn_vid cookie) — assigned on first pageview. Permanent per browser. UUID.
  2. User ID — your own internal ID for the user (however your app names them). Assigned when you call funnelfizz('identify', { userId }).
  3. Stripe customer IDcus_.... 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

NameStorageLifetimePurpose
_fn_vidCookie + localStorage365 daysPermanent visitor ID
_fn_sidsessionStorageTab lifetimeSession 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.doNotTrack is 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.