fastmon Docs
Concepts

Visitors and sessions

How fastmon counts visitors without identifying them, and what a session adds on top.

Fastmon counts visitors, not people. A visitor is a count along an identity axis, and there are two:

  • stitch (the default): an edge-derived identifier present whenever store_stitch is on (the Standard and Full presets). It is bounded-lifetime and forward-secret (see Privacy), so it lets the dashboard count unique visitors without a cookie or anything stored on the device.
  • session: the client-minted session_id (sid on the wire), present only when collect_sessions is on (the Full preset, or after your CMP calls grantConsent()). It groups a short burst of page-views from the same browser into a visit with a 30-minute boundary, which the visit-level views build on.

Which one the dashboard counts by default is the application's count_visitors_by setting (default stitch). That's a reporting choice, kept separate from what the site collects: if a site doesn't collect the chosen identity, the count falls back to whatever it does collect, and to nothing if it collects neither.

What the stitch lets you do

With stitch stored (the default Standard preset), and no cookie or consent needed, you get:

  • Unique-visitor counts over any time range.
  • New vs. returning within the identifier's lifetime. stitch rotates on its ~24 h salt schedule, so "returning" means within that window, not across weeks.
  • Per-visitor drill-down. Follow one visitor's page-view path across the site to debug their actual experience, in particular how performance holds up over time.
  • Visitor-level aggregates: page count, dwell time, average LCP / FCP and total errors per visitor (the Visitors view below).

What it doesn't give you: a long-term identity (it rotates), a link across applications, or the strict 30-minute visit boundary. That boundary, and the per-visit metrics built on it (pages per visit, bounce rate), come from the session identity (collect_sessions).

What counts as one session

A session contains the page-views that share all of these:

  • the same site_id,
  • the same session_id (sid on the wire), minted client-side per session and only sent when collect_sessions is on,
  • a gap of less than 30 minutes between consecutive page-views.

Without collect_sessions (and before grantConsent() on a deferred site) no sid is written, so those page-views carry no session. They still count as visitors under stitch when it's stored.

The session ends when:

  • 30 minutes pass without a new page-view, or
  • the date changes in the visitor's local time, or
  • the browser process closes (observed only via the gap rule).

A new page-view after the boundary starts a new session.

What's on a visitor

The Visitors view lists one row per visitor, grouped by the resolved identity:

FieldTypeNotes
idstringThe stitch hex or the session_id, depending on identity.
identityenumstitch or session: which identifier this row groups by.
started_attimestampFirst page-view's start.
ended_attimestampLast page-view's end.
duration_msnumberSpan from first to last page-view, in milliseconds.
page_countintNumber of page-views.
countryISO 3166-1 alpha-2Derived from the IP at the edge.
browserstringFamily only.
device_typeenummobile, tablet, or desktop.
osstringOperating-system family.
avg_lcpnumberAverage LCP across the visitor's pages.
avg_fcpnumberAverage FCP across the visitor's pages.
total_errorsintTotal errors across the visitor's pages.
outcomeenumHow the visit ended: ordered, checkout, cart, browsed, or bounced. The highest stage wins.
journeylistThe first 50 pageviews in order, each with page type, time on page, errors, checkout stage, and arrival. journey_truncated marks a longer visit.

Note what's absent: no user ID, no fingerprint, no IP. Neither identifier is meant to be used outside the analytics window: stitch rotates on its salt schedule (see Privacy), and session_id is short-lived.

Where a visitor came in

A journey step of POST /analytics/visitors and a page of /visitors/detail can carry arrival: arrival_type, traffic_source, traffic_medium, and utm_campaign. The UTM value wins over the referrer.

  • Entry: journey[0].arrival is always set, with arrival_type = first. Direct traffic has an empty source.
  • Return: a later step has arrival_type = return when the visitor came back from outside, such as a newsletter link after a Google visit.
  • No return: reloads, back and forward, SPA route changes, checkout pages, and referrers missing from the referrer catalog, such as another host of the same shop.

Several sites

A scope over several sites or the whole organization counts by the identity its applications store.

  • Applications disagree: the identifier most of them store counts, each application once. meta.is_identity_mixed is true when that makes the count a lower bound.
  • None stores one: visitors is null, not 0.

Querying visitors

The Analytics API exposes three endpoints:

  • POST /analytics/visitors: paginated list of visitors in a time range, grouped by the resolved identity, with filters. Cursor-paginated for high volumes.
  • POST /analytics/visitors/segments: how many visitors the list holds with errors, slow (average LCP at or above slow_lcp_threshold_ms), ordered, in the checkout, with a cart interaction, and with one page only. It takes the same scope and filters as the list.
  • POST /analytics/visitors/detail: the full page-view path for one visitor (the "replay"). It accepts identity=session or stitch (whichever the visitor list used; default session); an unknown identity is rejected with 422. On the Minimal preset the site collects neither identifier, so there are no visitors to drill into.

Pass identity explicitly to override the application default, or leave it null to use count_visitors_by. See Analytics API for the request shapes.

The list filters on what a visitor's journey contains: contains_page_type for a page type, and on shops contains_checkout_stage (cart, checkout, or success) and contains_cart_interaction for the checkout funnel. Each pageview in the detail carries its checkout_stage and cart interaction fields, and its own client context (country, browser, browser_version, device_type, os): per pageview rather than per visitor, because a stitch visitor can span devices, countries, and browsers, and a mixture should show as one. The funnel itself has its own endpoints under /analytics/funnels/checkout, grouped on the same identity.

Common surprises

  • Visitor counts follow store_stitch, not consent. When stitch is stored (the default Standard preset, and Full) you get unique-visitor counts without a cookie or consent. The Minimal preset stores no identifier, so it has neither visitor counts nor sessions. Turning on collect_sessions adds the session identity, which draws the 30-minute visit boundary the session-only views (pages per visit, bounce) build on.
  • A session in fastmon is shorter than in some other tools. Tools that set a 1- or 2-year cookie glue any return visit onto the previous session. We don't have that cookie, so a 30-minute gap is the hard boundary.
  • One visitor across two days is two sessions, and may also be two stitch visitors: the stitch value rotates on the salt schedule (see Privacy), so it is not a long-term identity either.
  • No "users" concept. There's nothing above the visitor. We don't store who someone is, and that includes "is this the same person who showed up last month?"
  • Bot traffic still counts. The collector filters the obvious bots, but a deliberate headless browser looks like a real visitor. Filter by device_type and set notification rules with sanity bounds.
  • Tracker settings: what the beacon collects per page-view, and which identities each setting produces.
  • Privacy: how the stitch identifier is derived and where its privacy guarantee actually comes from.
  • Analytics API: querying visitors programmatically.

On this page