fastmon Docs

Tracker settings

Every tracker setting on an application, what it collects, and a short PII read on each.

Each application carries a set of tracker settings that decide what the beacon does in the visitor's browser and what reaches storage. Every behavior is an independent switch, and a preset fills those switches with a starting point.

Settings live on the application and are switchable at any time. The script cache invalidates automatically; you don't redeploy your site or touch the <script> tag. A Shopify application is served a second, much smaller script for its checkout, see Track the Shopify checkout. A brand-new application starts on the Standard preset: unique-visitor analytics through the edge-derived stitch, fetch/XHR and query-key telemetry, and full error frames, all with nothing written to the visitor's device.

Each setting has its own section and a stable anchor, and ends with a PII line on what it means for personal data. For the legal picture see Consent and the TDDDG and Privacy.

Presets

A preset is a starting point you pick when you create an application. It fills every data-scope switch at once; after that each switch is independent, and changing any one of them just relabels it as Custom. New applications start on Standard. (Creating one directly through the API without naming a preset applies the bare per-field defaults, which match Minimal.)

SettingMinimalStandard (default)Full
collect_sessions✗✗✓
session_consentdeferreddeferredimmediate
store_stitch✗✓✓
collect_query_keys✗✓✓
collect_error_messages✗✗✓
collect_error_frames✗✓✓
collect_fetch_xhr✗✓✓
Nothing stored on device✓✓✗ (sessionStorage._fms)
  • Minimal collects core RUM only: Web Vitals, error type and count, device and connection class. No identifiers, no content, no fetch/XHR, and nothing written to the visitor's device. It is the opt-down when you want no visitor identifier at all.
  • Standard turns on everything that cannot carry personal data: the edge-derived stitch visitor signal, fetch/XHR aggregates, query key names, and full error stack frames. It keeps no error message text, no query values, and writes no session id or anything else on the device. A new application starts here.
  • Full turns everything on: a session id written on load, error messages and full frames, query keys. It is the only preset that writes to the device (sessionStorage._fms), so it presumes you have handled consent.

collect_query_values_for and the routing, retention, and site-management settings are not part of a preset; they default independently and you set them on the application.

Whatever an application collects, the beacon reads several browser interfaces on the visitor's device:

  • the Performance API (navigation, paint, layout-shift, event and long-animation-frame timings) for Web Vitals and timing,
  • the Network Information API (navigator.connection) for the connection class,
  • the viewport width (window.innerWidth),
  • the error and unhandled-rejection handlers (error type and count, collected in every configuration),
  • the navigator.webdriver flag, which a browser under automation sets itself, so a Puppeteer or Selenium run counts as a bot.

fastmon sets no cookies, in any configuration. In the default Standard setup it writes nothing to the visitor's device; only when collect_sessions is on does it write a single, tab-scoped sessionStorage entry (the session id). It stores no long-lived, cross-site, or cross-device identifier, and aggregation happens at the edge.

Which stored fields are and aren't personal data, and where a residual can't be fully ruled out, is laid out in Privacy → Personal data.

Reading interfaces like these can be consent-relevant under § 25 TDDDG and the ePrivacy Directive. The readings range from permissive (reading a browser API is not "accessing information on the device") to strict (analytics is not "strictly necessary", so consent is required). Germany and the EDPB lean strict, and EDPB Guidelines 2/2023 cover locally generated results of a browser API too.

Against that we hold the German statutory text, which requires information already stored, so we classify Minimal and Standard as consent-free. No court has settled this, so a residual risk remains.

The assessment for your site is yours to make, ideally with your data protection officer. If you gate fastmon, categorize it as analytics / performance measurement in your CMP, not as "strictly necessary". § 25 TDDDG and the GDPR are assessed in parallel, and the contractual split sits in our DPA (§ 4) and Terms (§ 7). Running fastmon under your own domain is the better setup either way, see collector_mode.

When an application writes a session id (collect_sessions on), your CMP switches the on-device write on with a single call after consent:

// In your cookie banner's "Accept" callback, and on every page load
// while consent is granted (most consent managers do this for you):
window.fastmon && window.fastmon.grantConsent && window.fastmon.grantConsent();

grantConsent() mints the sessionStorage._fms session id and unlocks cross-page linkage from that point on. Call it on every page load while consent stands; most consent-management platforms do this for you.

The && guards matter: the beacon loads asynchronously, so on a fast navigation your consent code can run before window.fastmon exists. Guarded, that early call is a no-op and the write happens once the tracker has loaded; unguarded it throws a TypeError. Whether the write waits for the call or happens on load is the session_consent switch.

There is no mid-session revoke: the session id lives in sessionStorage and dies with the tab.

Session and cross-page linkage

collect_sessions

Writes a session id (_fms) to the visitor's sessionStorage so pageviews of the same visit link together: cross-page journeys, funnels, entry and exit pages, return-within-session metrics. The id lives in sessionStorage and dies when the tab closes. It is the only thing the beacon ever writes to the device; with it off, the served bundle never touches sessionStorage at all (the storage step is compiled out rather than dropped server-side), and the collector forces the session id empty as a backstop. Off on the Standard preset new applications start on; the Full preset turns it on.

In the dashboard the switch is Store session ID under Visitors & sampling, and turning it on asks you to confirm first.

PII: the session id is a visitor identifier and writing it is the only operation by which the beacon puts anything on the device, which makes it consent-relevant under § 25 (1) TDDDG (see Consent and the TDDDG). Leave it off for the leanest posture, or pair it with session_consent to write only after opt-in. With it off, a row carries no on-device identifier.

Decides when the session id is written, and only matters when collect_sessions is on. Set to deferred (the dashboard shows this as Write session ID only after consent), the write is deferred until your CMP calls window.fastmon.grantConsent(); until then beacons still flow, just without a session id. Set to immediate, the id is written on page load. Reads, observers, and beacons run either way: this switch gates only the on-device write, never collection. Default deferred.

PII: this changes the timing and consent posture of the session write, not what categories of data are collected. deferred is the safe default (no device write before consent); immediate writes on load and presumes you have a lawful basis for that write.

store_stitch

Keeps the edge-derived stitch signal on the stored row. stitch is an HMAC-SHA256 of the visitor's IP, User-Agent and the application's collector hash, computed at the edge under a salt that rotates every 24 hours and never leaves edge memory. It gives you unique-visitor counts and visitor-level metrics (bounce rate, pages per visit, dwell time) without anything on the device. With it off, the collector forces stitch empty at insert time, leaving a row with no visitor identifier at all. It applies to every site of the application. On by default: the Standard preset new applications start on stores it. The full construction (forward secrecy, ≤24 h joinability, per-application scoping) is in Privacy → the edge-derived stitch.

PII: stitch is a pseudonymous visitor identifier and therefore personal data, processed under Art. 6 (1)(f) GDPR with a documented legitimate-interest assessment; joinability is bounded to roughly 24 hours and is forward-secret once the salt rotates. Naming it in your privacy notice is the operator's job.

count_visitors_by

Picks which identifier the dashboard counts distinct visitors by, stitch or session. It is a reporting default only, separate from the collection switches, and the query resolver clamps it to whatever the site actually stores (a per-query override is always available). Default stitch. It applies to every site of the application.

PII: none of its own. It selects between identifiers already collected under store_stitch and collect_sessions; it neither collects nor stores anything new.

URLs and query parameters

collect_query_keys

Keeps the names of query parameters on the page URL (for example ?page, ?variant), so you can break analytics down by parameterized routes. Parameter values are always dropped at collection regardless of this switch. Off drops the query string entirely, storing origin plus path only. On by default (part of the Standard preset).

PII: low. Only key names are kept, and every value is stripped before the URL leaves the browser. A fixed set of ad-attribution and click-id parameters (gclid, gbraid, wbraid, msclkid, fbclid, and the utm_* and gad_* families) is always dropped, key and value, on page URLs and fetch/XHR paths. Naming one in collect_query_values_for keeps it.

collect_query_values_for

An opt-in allow-list (max 20 entries) of parameter names whose value is additionally kept, length-capped. Everything not on the list still has its value dropped. Requires collect_query_keys. Typical entries are non-personal routing or campaign parameters such as page, variant, utm_campaign. Default empty. Not part of a preset. Shops whose checkout steps differ only in a query parameter, such as ?step=, list that key here so the checkout funnel can match on it. OXID's cl is kept without it.

PII: you own this one. A value is stored only for the exact keys you list, and never "all values". If you list a parameter that carries personal data (an email, a user id, a token), you have chosen to store it, so keep the list to non-personal keys.

template_url_ids

Replaces ID-ish path segments of the stored page URL with :id, so /order/12345 is stored and reported as /order/:id. A page report then stops spreading one page over thousands of URLs. Off by default, and not part of a preset. It is the rule the fetch/XHR endpoint paths already run: numeric, UUID, long hex and opaque token segments, plus an ID inside a literal segment. Long product slugs stay verbatim.

It applies at ingest and only to new rows, so a report spanning the switch shows both forms for a while. Turning it off later does not bring the raw URLs back. Checkout funnel matching and the error-frame strip keep seeing the real path, so the switch does not change which pageviews land in a funnel stage.

PII: this setting only removes data. An id in a page path is the operator's own doing, and this is the switch that stops it reaching a report.

Errors

What counts as an error

The tracker counts two populations apart, and they never mix:

  • JavaScript errors (errc on the beacon): uncaught exceptions and unhandled promise rejections. This is what the error rate is built from.
  • Failed resource loads (frc): an <img>, <script>, or <link> that did not load. Their own signal, with their own rate and their own list, because the population is dominated by ad blockers and tracking protection rather than by defects of the site. See Failed resource loads.

Both counters count every occurrence, and both stop at 1000 per pageview. A stored count of exactly 1000 means "1000 or more", and past the cap the tracker records no signature it has not already seen on that pageview. The collector clamps beacons from older tracker builds to the same ceiling.

The beacon carries up to eight ranked entries, the top five JavaScript errors followed by the top three failed resources, each ranked inside its own channel, so a page full of broken images cannot push a real exception off the wire.

First party means the same registrable domain as the page. A script served from cdn.shop.example to a page on www.shop.example is your own, and the rule is the same one for the error rate, the per-error first-party flag, the pageview's error origin, the Fetch/XHR split, and the resource list. What the privacy strip keeps is a stricter rule and unchanged: without collect_error_frames, a frame outside the exact page host keeps its host and nothing else.

ScriptError is the type for an exception the browser masks. A script from another origin loaded without CORS opt-in reports as the literal Script error. with no filename, line, or error object, so it cannot be attributed to a file and used to share a fingerprint with every other frameless error. Load the script with crossorigin="anonymous" from a host that sends Access-Control-Allow-Origin and the browser reports the real error instead. Do not set the attribute where the header is missing, because the browser then refuses to run the script.

What a message says

The browser's own wording is the diagnosis for a whole class of errors, and it is also the only free text the tracker ever collects. So the tracker recognizes the wording instead of sending it. A message that matches one of the engines' own phrasings yields a shape from a closed list, plus the identifier that shape names, where it names one:

ShapeWhat it says
undefined_read, null_read, undefined_writea property read or written on nothing, with the property name
not_defined, not_a_functiona name that does not exist, or is not callable, with the name
json_empty, json_invalida response parsed as JSON that was empty, or was not JSON
selector_invalida selector the browser rejected, with the selector
readonly_write, stack_overflow, fetch_failed, syntax_unexpected_tokena write to a read-only property, infinite recursion, a request that never completed, a parse error

The shape travels in every configuration, Minimal included, so an error reads as "uc.setServiceAlias was called, but uc does not exist at this point" on a site that keeps no messages at all. The raw message travels only with collect_error_messages on.

Where the raw message does travel, its placeholders stay readable. A stripped literal keeps its quotes, so a sample reads '*' is not a valid selector, and a removed email reads <email>. Numbers keep the bare *, and a URL survives as origin plus path, with query, fragment and numeric path ids removed.

Recognition runs in the browser, before anything is sent, which is what lets the message be gated at all. The name that travels with a shape is checked there too: an identifier has to look like code, a selector has its quoted parts blanked and is capped at 80 characters, and anything else is dropped rather than sent.

The shape is part of the error fingerprint, which is what makes the same fault one group across engines: Chrome and Safari word the same fault differently, and grouping used to follow the wording. Signatures were regrouped once when this arrived, so a group from before that day keeps its key and stops growing while the same fault reappears under a new one. There is no backfill, because the messages the new grouping reads were never stored.

collect_error_messages

Keeps the free-form error message text (Cannot read property 'x' of undefined) on top of the error type, the count, and the message shape. Off, the message is never assembled: the switch is baked into the served bundle, so on a site that did not ask for it the message does not reach the wire at all. Off by default, including on Standard; the Full preset turns it on.

PII: the highest free-text personal-data vector in the beacon, and consent-relevant. An always-on scrubber replaces URLs, emails, numeric ids and quoted strings before transmission, but the operator's obligation not to pass user input into thrown errors still stands. The message shape is not this field, and it is what the dashboards read where this switch is off. Turn it on only where you have handled consent.

collect_error_frames

Keeps full stack-frame detail (script path plus line and column) for every frame. Off, frames collapse toward host-only, except that your own first-party external scripts always keep path and line/column so own-code bugs stay fixable. On by default (the Standard preset turns it on).

PII: a code location identifies the bug, not the visitor, and it is identical for everyone who hits it, so first-party own-code detail is treated as always-sendable (sanitized to origin plus path, no query or hash). Full detail for inline and third-party frames is consent-relevant, because line numbers inside server-personalized HTML could in principle shift per visitor, so those stay gated. Paths are the operator's responsibility not to encode identifiers into, the same as page URLs.

Fetch and XHR

collect_fetch_xhr

Captures aggregated fetch/XHR telemetry: per-endpoint call counts, durations, error status codes, and the slowest outliers, so you can see which of your own APIs are slow or failing. A repeated failure with the same status on the same endpoint sends one fetch_xhr_update; the repeats only raise the error count that the next beacon carries. Baked into the served bundle at render time. Off suppresses fetch/XHR collection entirely, including the wrapper. On by default (the Standard preset turns it on). The checkout funnel reads its cart interactions from this telemetry (a POST to a known cart endpoint), so with this off the cart interaction counts stay empty while the URL stages keep working.

A new application stores the calls of a quarter of its visitors, see Fetch and XHR sampling.

PII: endpoint paths are sanitized like the page URL and in fact stripped harder. The collector re-templates them server-side: long content slugs to :slug, opaque tokens to :id, soft-navigation document fetches to /:page, and a third-party host loses its query string entirely. The counters are aggregate numbers, and the target host is site metadata. The one residual is an alpha segment like /users/jane-doe surviving templating, the same operator obligation as page URLs.

Page type

In the dashboard and on the API this is two settings: platform names the shop system, and page_type.body_class turns the read on. pagetype_ruleset is derived from the two. A <meta name="fm-page-type"> tag needs no setting. The tag, URL rules, and the order of all sources are under Applications → Page-type detection.

pagetype_ruleset

Classifies each pageview into a page type (product, category, cart, checkout, and so on) for shop systems, by reading the shop framework's body controller class. Off by default (""), which reads nothing from the DOM. Set it to a built-in ruleset (shopware6, shopware5, oxid, magento, woocommerce) and the tracker extracts the framework's page token, which the collector maps to a page-type category. A Server-Timing: pageType header always wins over this. Where your theme renamed one of the framework's classes, name it yourself with pagetype_rules. Per-site, and available on org-level trackers too. See Server-Timing → Page type. Without any other source, a checkout stage path gives cart or checkout.

PII: the only thing read from the DOM is a single framework class token, matched by a ruleset-scoped pattern; never the full class attribute, never page content or form values, and the raw class never leaves the browser.

What each ruleset detects

Each ruleset maps the framework's page-controller token to one of a fixed set of page types (home, product, category, cart, checkout, search, account). Tokens outside this table map to nothing, and the pageview keeps an empty page type. The mapping lives in the collector, so it improves without redeploying the browser-cached tracker.

Page typeShopware 6 1Shopware 5OXID eShopMagentoWooCommerce
homefrontend-home-page, is-ctl-navigation is-act-homeis--ctl-indexcl-startcms-index-indexnot typed 2
productfrontend-detail-page, is-ctl-product*is--ctl-detailcl-detailscatalog-product-viewsingle-product
categoryfrontend-navigation-page, is-ctl-navigation*is--ctl-listingcl-alistcatalog-category-viewpost-type-archive-product, tax-product_cat
cartfrontend-checkout-cart-page, is-ctl-checkout*is--ctl-checkout 3cl-basketcheckout-cart-indexwoocommerce-cart
checkoutfrontend-checkout-confirm-page, frontend-checkout-finish-page, frontend-checkout-register-pagefolded into cart 3cl-user, cl-payment, cl-ordercheckout-index-indexwoocommerce-checkout
searchfrontend-search-page, is-ctl-search*, is-ctl-suggest*is--ctl-searchcl-searchcatalogsearch-result-indexnot typed 2
accountfrontend-account-*, is-ctl-account*is--ctl-accountcl-accountcustomer-account-*woocommerce-account

1 Each cell lists the current token first: Shopware's is-active-route-* classes with the fixed prefix stripped, emitted since about 6.6 and the only page marker left in 6.8, which removes the is-ctl-* and is-act-* classes. The is-ctl-* tokens after it are the legacy fallback for older shops and browser-cached trackers, with * standing for any action tail. Home needs the exact legacy class pair, because the home page renders through the navigation controller like any category page. On the legacy classes, cart and checkout share is-ctl-checkout and both report the coarser cart, and is-ctl-account* covers the whole account controller family. 2 WooCommerce reuses generic WordPress classes for the home and search pages that are too broad to type reliably, so they stay untyped. 3 Shopware 5 shares one controller token across the cart and confirmation steps, so both report the coarser cart rather than a guessed checkout.

pagetype_rules

Names the body classes of your own theme, for the themes that renamed what the shop system ships. A built-in ruleset matches the classes its framework emits, so an OXID theme that writes c-body--details where the ruleset expects cl-details records no page type at all, and nothing says so. Each rule maps one exact class name to one page type (home, product, category, cart, checkout, search, account). Empty by default.

The rules sit in front of the ruleset instead of replacing it, in both steps (reading the token and mapping it), so you fix the one page type your theme renamed and the other six keep coming from the ruleset. With rules set and pagetype_ruleset empty, the tracker reads only the classes you named, which is how a shop system or CMS that has no ruleset gets a page type at all. A Server-Timing: pageType header still wins over both.

Class names are exact and compared as whole tokens, never patterns: the comparison runs in every visitor's browser, and a pattern there would be a hazard on your own page. At most 24 rules, [a-z0-9_-] and at most 32 characters per class name, each class only once. Upper and lower case do not matter (names are lowercased on both sides), unlike in the built-in rulesets.

Set it through the application API (pagetype_rules on POST or PATCH /applications) or in the page-type setting in the dashboard, which can also work the class names out for you.

PII: the narrowest form of the body-class read. With rules and no ruleset, the only token that can ever leave the browser is one you named yourself. The full class attribute is never read, and the read stays one token per pageview.

Finding your theme's class names

Two setup helpers answer the two questions the setting asks. Both run from the dashboard next to the setting and from the API, both read only the application's own domains, and both are limited to ten runs per organization per hour. Neither writes anything: each one proposes, and you confirm by saving.

  • Which shop system is this? POST /applications/{id}/detect-pagetype reads the application's own home page and names the matching ruleset, with the evidence it used and the page type it would have recorded for that page. Shopify and PrestaShop are recognized and flagged as not selectable: Shopify reports its page type through Server-Timing instead, PrestaShop has no ruleset yet. A home page that answers with a bot challenge is reported as a failed probe, not as "shop system not recognized".
  • Which class means "product"? POST /applications/{id}/pagetype/discover takes up to four of your pages, each labeled with what it is, reads them plus your home page as the contrast, and proposes the class that only that kind of page carries. Two examples of one type give the surest answer, because whatever differs between them cannot be the marker. A page type with exactly one candidate comes back ready to save; several candidates are listed and not turned into a rule, and a type whose theme marks nothing comes back empty rather than guessed.

Ingest and routing

site_policy

Decides what happens to a beacon whose page-URL host is not a registered site of this application. Set on the application.

  • open (default, "Suggest"): drops the beacon and lists the host as a suggested domain to adopt.
  • auto ("Auto-create"): creates a site for the host and keeps its traffic.
  • strict ("Ignore"): drops the beacon with no suggestion.

This holds for an application with a single site too, so a www. host beside the apex or a separate checkout host needs a site of its own. Checkout events and NEL and CSP reports follow the same rule. A hostless <noscript> pixel beacon is dropped.

PII: none.

collector_mode

Where the beacon POSTs its data: default (fastmon's shared collector), custom (an explicit endpoint you host or route, set in collector_endpoint), or relative (a host-less same-origin /c/<hash>, natural when you serve the collector from your own domain). Default default.

PII: none by itself. It is a routing choice. Note that a same-origin or custom endpoint changes the network path the request takes, which can matter for your own consent and first-party framing, but it doesn't change what the beacon collects.

collector_endpoint

The absolute collector base URL, used only when collector_mode is custom (for example metrics.example.com). It must be set for custom and empty otherwise. Default empty.

PII: none. Endpoint configuration only.

data_retention_days

How long raw beacon rows are kept before the storage layer drops them: 30, 60, 90, 180, or 365 days. Default 90. Aggregates computed during the window survive; the raw rows are deleted automatically at expiry.

PII: this is your storage-limitation lever. Shorter retention is stricter; pick the shortest window that still lets you compare across your deploy cycles.

Site management

tags

Free-form labels (max 10) for grouping related sites, such as env:production or team:growth. Labels only today; a cross-site analytics scope that reports on every site carrying a tag is planned. See Sites → Tags.

PII: none. Site configuration, not visitor data.

is_active

When off, beacons are still accepted but not written to the analytics tables, so you keep the site's history while pausing new ingest. Use it for pauses, not for cutting retention. Default on.

PII: none. It gates writes, not data categories.

environment

Marks the application as prod or dev. Default prod. Use dev to keep staging, preview, or throwaway applications clearly separate from your production traffic.

PII: none. It labels the application; it collects nothing.

source_version

Pins the beacon template version the application serves (v1 is current). Leave it on the current version unless you're deliberately holding an application back during a beacon upgrade.

PII: none. It selects which tracker build is served.

Applications and sites

These settings live on the application, the tracking embed itself, not on an individual domain. An application owns the served bundle, the two hashes, and every switch on this page, and it serves one domain or twenty: each domain is a site with its own dashboard and traffic. There is no separate "org-level tracker" any more; an application with several sites is that. See Applications.

A site overrides none of them. A domain that needs another data_retention_days, store_stitch or count_visitors_by belongs in an application of its own.

Changing settings

Open the application in fastmon, go to Settings, change what you need, and save. The change takes effect when the script cache rolls over: usually within minutes for active visitors, immediately for new sessions. You don't redeploy your site or change the <script> tag.

What's next

On this page