VoidhashDocs
DocumentationAPI reference
Dashboard
Get started
Introduction
Quickstart
Setup
Setup overview
Create a project
Products and perks
Paywall locations
CLI
Paywall components
Components
Develop a component
Editor panels
Component definition
Publishing
Installing and capabilities
SDKs
Swift
Kotlin
React Native
Web
Store setup
Connect the App Store
Connect Google Play
Connect Stripe
API libraries
Node.js
PHP
Rust
Go
Resources
MCP server
Meta ads
Migrate in observer mode
Delete a person's data
Web SDK
Web quickstart
Identify users
Track events
Page views and attribution
Stripe revenue
Web-to-app handoff
First-party proxy
Feature flags
Troubleshooting

Page views and attribution

Record page views and sessions, and attribute visitors and revenue to campaigns and ads.

The web SDK records which pages visitors see and which campaign or ad brought them to your site. Combined with your Stripe payments, this tells you the trials, conversions, revenue and refunds each campaign, ad set and ad produced.

Page views

The SDK records a $pageview event when the page loads and after every client-side navigation that changes the path, through history.pushState, history.replaceState or the back and forward buttons. Changes to the hash do not count, and a navigation that only changes the query string is not a new page view.

This covers single-page apps without any router code: Next.js (App Router and Pages Router), React Router, TanStack Router, Vue Router, SvelteKit, and any router built on the History API.

Every event carries the page's origin, path and title, the referrer's origin and path, and the screen and viewport size. The query string is not recorded, except for the campaign parameters below.

To record page views yourself, turn the automatic ones off and call page():

const voidhash = createVoidhashClient({
  analytics: { capturePageViews: false },
  publishableKey: "vh_pk_…",
});

await voidhash.page("Pricing", { experiment: "annual-first" });

Campaign attribution is recorded either way.

Upgrading from an earlier version

page() used to send a custom event named page. It now sends $pageview, the same event the SDK records automatically. If your app already calls page() on every route change, set capturePageViews: false, or every navigation is counted twice.

Self-hosted projects

$pageview is a built-in event that self-hosted installs do not store by default. Turn it on in the project's event settings, or page views are discarded when they arrive.

Create one client per project on a page. Two clients for the same project each record their own page views, so every page would be counted twice.

Sessions

Every event carries a session id. A session ends after 30 minutes without events, the same as on iOS, Android and React Native. All tabs of your site share the session, and reset() starts a new one.

Campaign attribution

A visit whose URL has any of these parameters is a campaign touch:

ParameterSource
utm_source, utm_medium, utm_campaign, utm_term, utm_content, utm_idAny campaign
fbclidMeta ad clicks
gclidGoogle Ads clicks
campaign_id, adset_id, ad_idAd ids you add to links

The SDK writes each touch to the visitor's person profile:

  • The first touch is kept forever, as the $initial_… traits.
  • Every new touch replaces the $latest_… traits.
Trait suffixValue
utm_*, fbclid, gclidThe URL parameters, unchanged
ad_networkmeta for fbclid or a Facebook, Instagram or Meta utm_source; google for gclid; otherwise the lowercased utm_source
ad_campaign_idcampaign_id, or utm_id when there is no campaign_id
ad_adset_idadset_id
ad_idad_id
landing_pathThe path of the page the visitor landed on, without the query string
referrer_originThe origin of the referring page
touch_atWhen the touch happened, in ISO-8601 UTC

For example, $initial_ad_campaign_id is the campaign of the visitor's first ad click and $latest_utm_source the source of their most recent campaign visit. The page view of the touch carries the same values as $utm_source, $ad_campaign_id and so on.

Reloading the page or navigating within your site with the same campaign parameters does not count as a new touch. Each touch writes every trait, with null for the values it does not have, so the first-touch and latest-touch traits always describe a single visit and never mix values from two. The page view of a touch carries only the values it has.

When a visitor signs in, identify() links their anonymous profile, including its campaign traits, to the user.

Meta ads

Add these URL parameters to your ads. In Ads Manager, set them under Tracking > URL parameters at the ad level:

utm_source=facebook&utm_medium=paid_social&utm_campaign={{campaign.name}}&utm_term={{adset.name}}&utm_content={{ad.name}}&campaign_id={{campaign.id}}&adset_id={{adset.id}}&ad_id={{ad.id}}

Meta fills in the {{…}} values when the ad is clicked, and adds fbclid itself. The ids give exact attribution to a campaign, ad set and ad even after you rename them.

The SDK also stores Meta's click and browser ids on the person, as $fbc and $fbp:

  • $fbc comes from the Meta pixel's _fbc cookie when it is present and less than 90 days old. Otherwise the SDK builds it from the fbclid of the visit, in Meta's fb.1.<timestamp>.<fbclid> format.
  • $fbp comes from the Meta pixel's _fbp cookie, when your site uses the pixel.

The SDK reads these cookies but never sets any.

Attribute Stripe payments

To credit revenue, renewals, and refunds to the campaign that brought the visitor, pass the visitor's Voidhash ID to Stripe when your server creates the Checkout Session or customer. See Stripe revenue.

Bots and crawlers

Events from well-known crawlers, link-preview fetchers, uptime monitors and headless browsers are discarded when they arrive, so they do not count as page views, sessions or campaign touches.

PreviousTrack eventsNextStripe revenue

On this page

Page viewsSessionsCampaign attributionMeta adsAttribute Stripe paymentsBots and crawlers