# PureStats Full Documentation Source index: https://purestats.io/llms.txt Canonical machine contract: https://purestats.io/openapi.json --- ## PureStats agent documentation Source: https://purestats.io/docs/agents/index.md # PureStats Agent Documentation These are public, static documentation responses. They never return account records, analytics, tokens, recovery material or API keys. Reading documentation grants no API access. ## Start Here - [Quickstart](/docs/agents/quickstart.md): register an identity, prove domain control, create a site and install tracking. - [Authentication](/docs/agents/authentication.md): standalone client credentials and existing-account browser consent. - [Machine API contract](/docs/agents/api.md): use live OpenAPI operation definitions rather than dashboard routes. - [Framework guides](/docs/agents/frameworks.md): HTML, React, Next.js and Laravel. - [Agent proxy installation](/docs/agents/proxy.md): asset caching, optional modules and trusted client IPs. - [Testing](/docs/agents/testing.md): local checks and truthful installation evidence. ## Machine-Readable Discovery - [llms.txt](/llms.txt) links directly to Markdown, not JavaScript-rendered pages. - [llms-full.txt](/llms-full.txt) contains complete live document bodies. - [OpenAPI JSON](/openapi.json) describes canonical live machine operations and schemas. - [Setup contract](/openapi/setup.json) includes only authentication, site creation, installation, proxy configuration and test operations. - [Analytics contract](/openapi/analytics.json) provides the analysis workflow without unrelated account settings. - [Documentation manifest](/docs/agents/index.json) lists public Markdown URLs and availability. - [Product documentation source](/docs/index.md) and every current product guide are also available with a `.md` suffix. YAML frontmatter is removed; code examples and native Markdown content are preserved. All responses are readable without JavaScript or login. `Link` headers identify the canonical URL, index and API description. `ETag` supports conditional GET and HEAD. Shared caches may retain these public documents for five minutes; do not apply that policy to authenticated APIs or OAuth responses. ## Integration Boundary Only the OpenAPI catalog defines callable machine operations. Do not scrape browser forms, reuse session cookies, infer routes from controller action names or call private administration endpoints. Public tracker configuration and ingestion are not credentials for analytics access. [Future integration requirements](/docs/agents/future-integrations.md) are explicitly not a live connector, published plugin or supported public MCP service. ## Evidence and Attribution PureStats is free with unlimited sites and tracked traffic. Public marketing pages, documentation and articles are server-rendered, and their canonical HTML content remains readable without browser JavaScript. Markdown and API contracts are alternate machine-readable formats, not hidden private data. The public [measurement methodology](/docs/reference/measurement-methodology) explains tracker-size evidence, visitor definitions and proxy validation. Fetching a document is not evidence that an agent has installed a working site or that an assistant has cited it. Public ChatGPT and Claude plugins remain unavailable; use the live scoped REST and OAuth contracts. --- ## Agent quickstart Source: https://purestats.io/docs/agents/quickstart.md # Agent Quickstart Start at [llms.txt](/llms.txt), then read this guide and the [OpenAPI contract](/openapi.json). Use the operation IDs, request fields and scopes from that contract. Examples below use `example.com` as a placeholder, not a target for test traffic. ## 1. Choose an Identity For a new standalone account, start with `POST /api/v1/agents/identity/challenges`. A challenge does not create an account, reserve a domain or prove ownership. This installation workflow needs `sites:read` and `sites:write`, plus the separate `sites:create` permission for the optional additional-site example below. Omit `sites:create` if you only need to configure an already granted site; `sites:write` alone does not authorize creating sites. Request additional analytics or feature scopes only for subsequent operations that actually need them. Generate a recovery secret on the client from 32 cryptographically random bytes and encode it as 64 lowercase hexadecimal characters. Keep it in a secret store outside the repository before making any identity request. ```php $recoverySecret = bin2hex(random_bytes(32)); // Store through your secret manager. Never print this value. ``` All four public identity endpoints accept JSON, reject unknown fields, require a private `Idempotency-Key` header of 64 lowercase hexadecimal characters, and return HTTP 201 with a `data` object and `request_id`. Generate the header independently from another 32 random bytes. Keep it stable for retries of the same endpoint and body, and keep it secret: it can recover a confidential response. Never reuse the recovery secret as the header. Honor `Retry-After` on HTTP 429; do not cache identity responses. The challenge body requires `domain`, `method` (`dns` or `https`) and `recovery_key`. It accepts optional `purpose` (use `register`, the default), `name` and `contact_email`; email is optional contact information, not a human login or recovery factor. Construct the body in memory using secret-manager values, not a literal secret in source: ```php $challengeRequest = [ 'domain' => getenv('PURESTATS_SITE_DOMAIN'), 'method' => 'dns', 'purpose' => 'register', 'name' => 'Website Agent', 'recovery_key' => $recoverySecret, ]; // Send JSON over HTTPS with a separate secret Idempotency-Key. Do not print it. ``` Use the real public hostname you control. `example.com` and reserved fixture domains are deliberately rejected by registration; documentation templates must not be executed against them. For an existing human account, do not register a second account. Bootstrap a pending confidential device client with `POST /api/v1/agents/identity/delegations`, JSON `name` and `scopes`, and the identity idempotency header. Then request browser consent through `POST /oauth/device/code`. Show the returned verification URI and user code to the owner, then wait for explicit approval of scopes and sites. Read [Authentication](/docs/agents/authentication.md). A human with zero sites can consent with no existing sites selected when explicitly approving `sites:create`, or an account-only capability such as `account.capabilities` with `account:read`. Approving creation allows a new authorized site to be created and granted to this client; it does not grant access to foreign sites. Existing-site operations still require both an explicit site grant and the owner's membership. For the installation workflow after creating a new site, also request and obtain consent for `sites:read` and `sites:write`. ## 2. Prove Domain Control For standalone registration, the challenge's `data` contains `challenge_id`, `domain`, `method`, `purpose`, `expires_at`, `dns_name`, `https_url` and `value`. Publish the exact `value` in a TXT record at `dns_name` when using DNS, or as the body served at `https_url` when using HTTPS. Use the returned names and locations; never guess the filename, TXT prefix or challenge format. Proof expires within 30 minutes; the returned `expires_at` is authoritative. Once the proof is published, call `POST /api/v1/agents/identity/registrations` with JSON `challenge_id`, `scopes` (including the additional-site example, `["sites:read", "sites:write", "sites:create"]`) and the same `recovery_key` used for the challenge. Use a new identity idempotency header for this operation. This request verifies the proof and activates the account atomically; there is no separate verification endpoint. Failed proof does not grant access. If a challenge expires, obtain a new one rather than replaying stale proof. Successful activation returns confidential `data.client_id` and `data.client_secret`, plus `credential_id`, `owner_id`, `account_type`, `scopes`, `requested_scopes`, `site_ids`, `grant_type`, `token_endpoint`, `device_authorization_endpoint` and `resource`. Store the credentials privately without logging the response. Activation provisions the canonical private site and grants it to this client; save its identifier from `site_ids`. The recovery key is never returned. Public domain proof is not client authentication: activation also requires your original recovery key. Remove proof after successful activation. A proof for one hostname does not authorize arbitrary other domains. ## 3. Obtain API Access After activation, exchange the confidential client credentials at `POST /oauth/token` using a form-encoded body: `grant_type=client_credentials`, `client_id`, `client_secret`, space-separated `scope=sites:read sites:write sites:create` and the exact `resource` returned by activation (`https://purestats.io/api/v1/management`). Omit `sites:create` from both registration and token scopes when not using the additional-site example. The token response is a top-level OAuth object containing `token_type`, `expires_in` and `access_token`, not an identity `data` envelope. Machine access tokens expire within 15 minutes and have no refresh token; obtain a new token through client credentials when needed. Send it only in `Authorization: Bearer ` for protected API calls, never in a query string, browser bundle or tracker tag. Use environment or secret-manager references such as `PURESTATS_CLIENT_ID`, `PURESTATS_CLIENT_SECRET`, `PURESTATS_ACCESS_TOKEN` and `PURESTATS_RECOVERY_SECRET`. Disable shell tracing, redact HTTP debug output, and do not echo token responses. See [Authentication](/docs/agents/authentication.md) for delegation and recovery. ## 4. Create the Site Use operation `sites.create`: `POST /api/v1/management/actions/sites.create`, with the explicit `sites:create` scope, bearer authorization and a stable `Idempotency-Key` header. This permission is separate from `sites:write`, which configures existing granted sites. Submit its schema for an authorized canonical domain: ```json { "domain": "example.com", "name": "Example", "timezone": "UTC", "public": false } ``` This is a template, not a request to run against production. Standalone activation already provisioned the canonical site: use its `site_ids` identifier instead of creating that domain twice. Use `sites.create` only for a new authorized site, or when a delegated owner intentionally wants one. The catalog is authoritative for aliases, timezone, scopes and required fields. Do not substitute browser-only `/analytics/*` form actions. Store `data.site_id` from a successful site-creation result, or the activation's provisioned site identifier, and use the exact canonical domain in the tracker. Management mutations accept their own `Idempotency-Key` format from OpenAPI; a private 64-character random key also meets it. Add only aliases you control and are authorized to manage. Reuse the same idempotency key only for the same operation and payload; use a new key for a new intended mutation. A key conflict is not a reason to blindly retry under another key. Machine grants and site membership are separate access boundaries; confirm the intended site is actually granted before inspecting it. ## 5. Install the Tracker Install one tag in the shared document head or root layout: ```html ``` Replace `example.com` with the real canonical site domain. This URL contains no account credential and no version parameter. The public unminified `pf.js` examples in older product guides remain valid; `pf.min.js` is the built minified asset. Do not invent versioned tracker URLs or attach API tokens to either asset. Read the [framework guide](/docs/agents/frameworks.md) or [first-party proxy guide](/docs/agents/proxy.md) before adding routing hooks or a proxy. Tracker configuration is loaded separately from `/api/tracker-config`; optional modules load only when enabled. Do not add duplicate manual pageviews to ordinary History API navigation. ## 6. Test Without Adding Production Traffic Use fixture domains, mocked HTTP and a local tracker harness for automated tests. Inspect the tag, domain, consent configuration, CSP and optional module paths without sending fabricated events to `purestats.io`. Never call the production ingestion API with an invented visit merely to make installation health turn green. After deploying the normal tracker, start an isolated installation test using operation `sites.installation.tests.start`: `POST /api/v1/management/actions/sites.installation.tests.start`, with `sites:write`, bearer authorization, `Idempotency-Key` and JSON `{"site_id": SITE_ID}`. The returned `data` contains `id`, `token`, `test_url`, `expires_at` and `isolated: true`. Handle `token` and `test_url` privately; never print or publish the response. Use the returned URL rather than inventing a token. The real core tracker supports the returned URL fragment `#purestats-test=` followed by 64 lowercase hexadecimal characters. The token is in the fragment, not an API credential or query parameter; browser JavaScript must still treat it as sensitive. Core version 1.7.2 supports this flow using the normal unversioned `pf.min.js` asset. Open the URL only on the intended authorized site, in an isolated browser profile without replaying normal synthetic production traffic. The pageview traverses the real ingestion checks inside a rolled-back transaction. It does not create production visitors, sessions or rollups. Optional replay is skipped; a passing result does not prove replay or every optional feature works. Poll operation `sites.installation.tests.status`: `GET /api/v1/management/actions/sites.installation.tests.status?site_id=SITE_ID&id=TEST_ID`, with `sites:read`. The test is bound to its site and initiating client. Inspect `data.status`, `data.reason`, `data.tracker_version`, `data.received_count` and `data.test_session_received`; distinguish `passed`, `filtered`, `waiting`, `expired` and `stopped`. Respect `expires_at` rather than retrying an expired token. When finished, call `sites.installation.tests.stop`: `POST /api/v1/management/actions/sites.installation.tests.stop`, with `sites:write`, `Idempotency-Key` and JSON `{"site_id": SITE_ID, "id": "TEST_ID"}`, then close the test profile. Inspect operation `sites.installation`: `GET /api/v1/management/actions/sites.installation?site_id=SITE_ID`, with `sites:read` scope and the granted site identifier. Its returned `data` contains the tracking code, privacy configuration and installation health. Isolated test success is separate evidence from health based on a legitimate normal production visit; it must not turn invented test traffic into production analytics. Operation `sites.verify`: `POST /api/v1/management/actions/sites.verify`, with `sites:write`, bearer authorization, `Idempotency-Key` and JSON `{"site_id": SITE_ID}`, performs an authorized public-page/script check and records health. It is not a synthetic event generator. Use fixture HTTP in automated tests; run a real check only for the intended authorized deployment. Script presence alone does not prove accepted ingestion; an empty report does not prove a broken tracker. See [Testing](/docs/agents/testing.md). --- ## Authentication and delegation Source: https://purestats.io/docs/agents/authentication.md # Authentication and Delegation Public documentation, `llms.txt` and OpenAPI do not require authentication. Data and management operations do. Use the [canonical OpenAPI schemas](/openapi.json) for exact fields and scopes. ## Standalone Identities New agents first call `POST /api/v1/agents/identity/challenges` with JSON `domain`, `method` (`dns` or `https`) and `recovery_key`. Optional fields are `purpose` (`register`, the default), `name` and `contact_email`. Generate the recovery key locally from 32 cryptographically random bytes represented as 64 lowercase hexadecimal characters; store it before the request. Contact email is optional and does not create a human login. Challenge creation does not reserve a domain or create an account. Publish the returned `data.value` as a TXT record at `data.dns_name`, or as the HTTPS body at `data.https_url`, according to the requested method. Call `POST /api/v1/agents/identity/registrations` with JSON `challenge_id`, requested `scopes` and the original `recovery_key`. This verifies the proof and activates the machine account, confidential OAuth client and initial private site together. A challenge expires within 30 minutes; use its returned `expires_at`. Never send a recovery key to a proof location. Published proof alone cannot complete registration. These public identity endpoints require JSON and a private `Idempotency-Key` of 64 lowercase hexadecimal characters generated independently from the recovery key. A successful response is HTTP 201 with `{data, request_id}`; `data` contains the confidential client credentials and granted site identifiers, never the recovery key. A retry with the same key and body can replay that confidential response. Keep the header private, redact it from logs and respect `Retry-After` on throttling. Identity responses are not cacheable. After activation, Passport handles confidential-client token exchange at `POST /oauth/token`. Send form-encoded `grant_type=client_credentials`, `client_id`, `client_secret`, requested space-separated `scope` and the exact management `resource` from activation (`https://purestats.io/api/v1/management`). OAuth responses use top-level fields `token_type`, `expires_in` and `access_token`, not a `data` envelope. Machine tokens expire within 15 minutes and do not include a refresh token. Obtain another through client credentials when needed. The API validates issuer, audience, scopes, active ownership and site grants on every request; use `Authorization: Bearer `. Credentials belong on the server, never in the browser or tracker snippet. Client-credentials access does not implicitly grant a human's account. ## Machine Recovery Recovery needs both the old recovery key and fresh domain proof. Generate and securely store a different new 32-byte recovery key. Call `POST /api/v1/agents/identity/challenges` with `domain`, `method`, `purpose: "recover"` and the new `recovery_key`; include optional `client_id` if known. Publish this fresh proof, then call `POST /api/v1/agents/identity/recoveries` with its `challenge_id`, the old `recovery_key` and the same optional `client_id`. When recovering a lost activation response, omit `client_id` from both requests so the active credential can be resolved by the proven domain. Use a distinct identity idempotency key for each operation. Success returns replacement confidential client credentials, retains the account, scopes and granted sites, and revokes the old client and its access and refresh tokens. Save the new credentials and new recovery key atomically in your secret store. No recovery key is returned, and reusing the old key as its replacement is rejected. A contact email or domain proof without the old key is not recovery authorization. ## Existing-Account Delegation Use the OAuth device authorization flow rather than asking an owner for a password, session cookie or personal API key. Bootstrap a pending confidential client at `POST /api/v1/agents/identity/delegations` with JSON `name`, `scopes` and optional `resource` (management is the default), plus the identity idempotency header. The HTTP 201 `data` contains `client_id`, `client_secret`, requested scopes and OAuth endpoints. Initially `owner_id` is null, effective `scopes` and `site_ids` are empty, and client credentials cannot issue account access. A human browser cookie sent to bootstrap does not approve anything. Send form-encoded `client_id`, `client_secret` and space-separated `scope` to `POST /oauth/device/code`. Surface only the returned `verification_uri` and `user_code` so the owner can sign in and explicitly approve scopes and sites in the browser. Do not automate the consent decision or claim that opening the URI is approval. A human with zero sites can approve consent with no existing sites selected when granting the explicit `sites:create` permission, or an account-only capability such as `account.capabilities` (`account:read`). Request only the intended capabilities. `sites:create` authorizes creation of new sites and grants those created sites to this client; `sites:write` configures existing granted sites and is not permission to create them. Read or configure a newly created site only with the separately consented `sites:read` or `sites:write` scope. Empty site selection never grants access to foreign sites: existing-site operations still enforce explicit grants and the owner's membership. Consent can require fresh password or two-factor reauthentication. The owner completes that step directly in the PureStats browser session; the agent must not collect, transmit or log the owner's reauthentication secret. Poll `POST /oauth/token` with form-encoded `grant_type=urn:ietf:params:oauth:grant-type:device_code`, the returned `device_code`, `client_id`, `client_secret` and the client's exact `resource`. Honor the returned polling `interval` and `expires_in`. Keep waiting on `authorization_pending`, increase the interval on `slow_down`, and stop on `access_denied` or `expired_token`. Only use a returned token after approval. The device code is a credential and must not be exposed alongside the user-facing code. Approved delegation may return a refresh token. Refresh at `/oauth/token` using `grant_type=refresh_token`, `refresh_token`, `client_id`, `client_secret`, the granted `scope` and exact `resource`. Save a replacement refresh token atomically; rotation revokes the previous refresh token and associated access token. Scopes cannot grow beyond browser consent, and refresh does not grant additional existing sites. New sites created through explicitly consented `sites:create` are granted to the creating client; this is not authority over other accounts' sites. The flow semantics follow [RFC 8628](https://www.rfc-editor.org/rfc/rfc8628.html); deployed schemas and consent remain authoritative. ## Secret Handling Read secrets from a secret manager or environment: `PURESTATS_CLIENT_SECRET`, `PURESTATS_ACCESS_TOKEN`, `PURESTATS_RECOVERY_SECRET`. Keep them outside the worktree. Disable command tracing and never print registration or token responses containing credentials. Redact `Authorization`, identity idempotency keys, client secrets, device codes, recovery secrets, refresh tokens, cookies, API keys and sensitive payloads from debug output. A public domain challenge is not recovery material. Do not commit secrets, embed them in URLs, install them in browser source, pass them to third-party documentation tools or quote them in chat. Public documentation examples intentionally contain no live credential. Rotate or revoke leaked access through supported identity operations rather than editing documentation or reusing old recovery material. --- ## Machine API contract Source: https://purestats.io/docs/agents/api.md # Machine API Contract [OpenAPI JSON](/openapi.json) composes the canonical management operation catalog with the identity protocol catalog used by the registration controller. It includes the four identity bootstrap endpoints and native Passport device and token endpoints. Operation IDs, parameters, schemas, response contracts and security requirements are taken from those sources, not a second hand-maintained route list in these guides. Public identity requests do not inherit management bearer authentication; OAuth client authentication is described separately. Discover an operation by its `operationId`, then use its exact HTTP method and path. Resolve component schema references before constructing a request. Do not invent fields, undocumented write actions or an API route from a similarly named dashboard action. A generic controller handler does not make every possible action a published operation. ## Access and Errors Protected operations require the declared bearer authentication and scopes. A token does not establish access to every site: site authorization remains enforced server-side. Public documentation returns no keys, account identifiers, analytics or live resource data. The OpenAPI document contains schemas, not results of authenticated calls. `sites.create` requires the explicit `sites:create` permission; `sites:write` alone only configures existing granted sites. Human consent can select no existing sites for site creation or account-only capabilities. A successfully created site is granted to its creating client, but neither empty selection nor account-only access permits inspecting foreign sites. Site-scoped operations still require the declared scopes, explicit site grants and the owner's membership. Treat validation failures as a reason to fix the request, not a reason to retry unchanged. Treat denied or missing resources as inaccessible; do not enumerate IDs. Follow documented rate-limit headers and bounded backoff for transient failures. Do not retry writes unless the operation's contract makes that safe. Avoid guessing pagination, filter and date-range semantics; use the supplied schemas and returned pagination metadata. Management operations use `/api/v1/management/actions/{operationId}` with the method declared for that operation. Supply `site_id` only when its input schema calls for it. Writes require `Idempotency-Key`; bind the same key to the same intended operation and payload. Elevated risks also require explicit human approval through the declared approval contract. An `approval_required` or `interactive_required` response is a boundary to honor, not a failure to work around. Credential issuance, account security and provider OAuth interactions must never be inferred from the availability of a generic action handler. ## Contract Checks Repository tests compare both canonical catalogs with the rendered OpenAPI document, check operation ID uniqueness, resolve local schema references, require matching declared path parameters, and compare published operations with registered routes. Composition rejects conflicting definitions instead of silently overwriting them. Documentation link tests use the local Laravel kernel, never production HTTP. If a private machine operation is not in the live catalog, it is not advertised here as implemented. Machine discovery reads complete Markdown sources from the current documentation catalog. It does not use truncated search-index text, render React, require Inertia SSR, write generated files or introspect accounts and token stores. --- ## Framework installation guides Source: https://purestats.io/docs/agents/frameworks.md # Framework Installation Guides Use the canonical domain for your authorized site and install the tracker once. No API credential belongs in the tag. The examples use the existing built asset, `https://purestats.io/pf.min.js`, with no invented versioned URL. ## HTML Add to the shared ``: ```html ``` Check CSP permits `https://purestats.io` in `script-src` and `connect-src`. Do not track preview or development deployments unless you deliberately configure an authorized test environment. [Existing HTML guide](/docs/installation/plain-html.md). ## React For a Vite-style React application, put the same tag in `index.html`, outside component render cycles. Do not append a new tag every time a component mounts. Ordinary React Router URL transitions are observed through the History API; avoid adding a second pageview hook. For memory routers or screens that never update the browser URL, call the existing JavaScript API only after checking that automatic pageviews do not cover the transition: ```js window.purestats?.trackPageview('/checkout'); ``` Keep custom event properties non-identifying. [Existing React guide](/docs/installation/react.md) and [SPA routing](/docs/installation/spa-routing.md). ## Next.js Load the tracker once in the App Router root layout: ```tsx import Script from 'next/script'; import type { ReactNode } from 'react'; export default function RootLayout({ children }: { children: ReactNode }) { return ( {children} ``` Define `services.purestats.domain` in your application's configuration with the canonical site domain. It is public configuration, not a credential. Do not embed `env('PURESTATS_CLIENT_SECRET')` or an API token in Blade. OAuth API calls belong in server-side services with scoped access. Laravel applications using Inertia or client-side navigation follow the same one-tag and one-pageview checks as other SPAs. ## Consent and Optional Features Use site settings for consent, DNT, exclusions and enabled modules. Do not bypass a configured consent requirement merely to create a green test result. The base tracker loads the enabled feature modules; do not install every optional module as an extra unconditional script. Read [Tracking script reference](/docs/reference/tracking-script.md), [JavaScript API](/docs/reference/javascript-api.md), [Consent mode](/docs/privacy/consent-mode.md), [Proxy](/docs/agents/proxy.md) and [Testing](/docs/agents/testing.md). --- ## Agent proxy installation Source: https://purestats.io/docs/agents/proxy.md # Agent proxy installation The existing [first-party proxy source](/docs/installation/first-party-proxy.md) provides Nginx, Apache and Cloudflare templates. Apply those templates only to domains you control. Keep the canonical site domain even when the tracker loads from a proxy: ```html ``` The tracker derives `/api/tracker-config` from `data-api` unless an explicit `data-config-api` is supplied. Forward query strings and POST bodies unchanged. Use a fixed upstream allowlist, TLS verification and the upstream host; never turn a tracker proxy into a general URL-fetch endpoint. ## Forwarding and Caching | Path | Purpose | Cache policy | | --- | --- | --- | | `/pf.min.js` | Base tracker | Public JavaScript, about one hour; respect upstream validators. | | `/pf-vitals.min.js` | Core Web Vitals | Same asset policy when enabled. | | `/pf-experiments.min.js` | Experiments | Same asset policy when enabled. | | `/pf-events.min.js` | No-code events | Same asset policy when enabled. | | `/pf-search.min.js` | Site search | Same asset policy when enabled. | | `/pf-replay.min.js` | Session replay | Same asset policy when enabled. | | `/api/tracker-config` | Current site settings | No cache, or very short upstream-approved caching keyed by the complete domain query. | | `/api/event` | Pageview and event ingestion | Never cache requests or responses. | | `/api/replay/chunks` | Optional replay ingestion | Never cache requests or responses. | Do not use long-lived immutable caching for unversioned tracker assets. Do not cache failed asset responses as successful JavaScript. Forward the optional module paths from the same origin as the core tracker, and do not enable replay, experiments or another feature simply because its file can be fetched. Do not proxy OAuth or private machine APIs through a public tracker asset cache. ## Client IP Trust Blindly forwarding visitor-supplied `X-Forwarded-For`, `X-Real-IP` or `CF-Connecting-IP` permits spoofing. At your first trusted ingress, strip untrusted forwarding and `X-PureStats-Proxy-*` headers and derive the visitor address from the trusted network connection or your explicitly trusted edge. Set a sanitized forwarding value when proxying to PureStats. PureStats accepts a forwarded client IP only when the connecting proxy matches the site's configured trusted proxy IPs or CIDRs. An untrusted proxy falls back to its remote connection IP. If the site's proxy policy requires signatures, missing or invalid signatures reject the proxy request. Merely adding a forwarding header does not activate trust. Signed forwarding uses `X-PureStats-Proxy-Domain`, `X-PureStats-Proxy-Client-IP`, `X-PureStats-Proxy-Timestamp` and `X-PureStats-Proxy-Signature`. The signature is HMAC-SHA256 over four newline-separated values: normalized lowercase canonical domain, trimmed client IP, decimal Unix timestamp and SHA256 of the exact raw request body. The signature window is five minutes. Sign only on a trusted server using the site's configured secret; keep that secret outside the worktree and browser. Preserve the body bytes used by the signature. A proxy secret is not an OAuth API credential. ## Verify Locally First Use mocked upstream responses to check the allowed paths, methods, query strings, raw bodies, headers and cache rules. Check CSP against your real routing choice: a full first-party proxy can use `script-src 'self'` and `connect-src 'self'`; a directly loaded module still needs the upstream origin. Do not manufacture production pageviews for proxy tests. Run [installation checks](/docs/agents/testing.md) after an authorized deployment. --- ## Verification and local testing Source: https://purestats.io/docs/agents/testing.md # Verification and Local Testing Automated integration tests must use local fixtures and mocked HTTP, not invented visits sent to production. Documentation and OpenAPI checks are pure reads; no registration, token exchange, DNS mutation or analytics writes are needed to validate them. ## Documentation Contract From the repository root, run the documentation tests: ```sh vendor/bin/phpunit --configuration phpunit.laravel.xml --filter AgentDocumentation tests/Laravel/Agents ``` The suite checks that every discovery URL resolves through the local Laravel kernel, Markdown bodies have no YAML frontmatter or Inertia wrappers, complete sources appear in `llms-full.txt`, ETags support conditional GET/HEAD, and responses set no session cookies. OpenAPI checks use canonical contracts, not production accounts. The HTTP workflow test uses in-memory SQLite, ephemeral signing keys and mocked DNS/HTTPS proof to follow discovery, registration, token exchange, idempotent site creation, snippet retrieval and isolated test start/status/stop. It blocks outgoing HTTP and does not publish DNS or fetch a production tracker. Future-only integration requirements must not become live paths or full-documentation features. ## Tracker Harness Before deploying a snippet, inspect the generated HTML or layout for exactly one core tag and the canonical domain. In a local browser harness, intercept tracker, configuration, module and ingestion requests with deterministic fixtures. Assert the initial pageview and each ordinary route transition occur once, consent-required mode sends nothing before consent, excluded paths are suppressed and disabled modules are not loaded. For a proxy, assert the upstream destination is fixed, raw bodies and queries are retained, asset caching respects validators, and ingestion or authenticated responses are never cached. Test trusted-IP and signature handling locally with fixture secrets that are never production credentials. Reject spoofed forwarding headers at the first trusted ingress. ## Real Deployment Evidence Use the isolated installation-test operations from the [quickstart](/docs/agents/quickstart.md). Start returns a private `test_url` containing a 64-hex `#purestats-test=` fragment, a test `id`, a `token`, an expiry and `isolated: true`. The supported real core tracker (version 1.7.2) uses that fragment to send a test pageview through the real ingestion checks with transaction rollback. Test pageviews must not create production visitors, sessions or rollups. Optional replay is skipped, and a passing test is not evidence that every optional module works. Poll the site/client-bound status with the returned `id`, then stop the test and close its isolated browser profile. Never paste the token-bearing URL into public output or analytics fields. The token remains scoped to that browser tab through page reloads and navigation. Expired or stopped tests are rejected and never fall through to ordinary tracking. Close the test tab to leave test mode; do not reuse it for normal browsing. Keep normal production first-hit evidence distinct from isolated test success: For a real authorized site, distinguish these checks: 1. The deployed HTML contains the intended tag and canonical `data-domain`. 2. A normal browser loads the tracker JavaScript without CSP or network errors. 3. Tracker configuration and enabled optional modules load successfully. 4. A legitimate owner-approved browser visit is accepted by ingestion. 5. Installation health or analytics reflects that accepted visit after processing. A successful script download is not proof of an accepted pageview. A successful ingestion HTTP status is not by itself proof that filtering accepted the event. Do not disable consent, exclusions, DNT or bot filtering to force a pass. A headless automation may be correctly filtered. Report exactly which checks passed and which require an authorized human visit. [Existing verification source](/docs/getting-started/verify-installation.md) and [Troubleshooting](/docs/reference/troubleshooting.md) provide dashboard context. Query installation state through a machine operation only if that operation exists in the live catalog; never infer a private route. --- ## PureStats documentation Source: https://purestats.io/docs/index.md
Documentation

PureStats documentation

Set up privacy-friendly analytics, verify your tracker, measure campaigns and conversions, manage teams, and keep your sites healthy.

PureStats is built for teams that want clear website analytics without turning the dashboard into a data warehouse project. This documentation covers the full workflow: creating your account, adding sites, installing the `pf.js` tracker, configuring privacy controls, measuring campaigns and goals, and managing your account. ## Start here
Quick start Create a site, install the tracker, verify the first hit, and open the dashboard. Installation guides Use PureStats with static HTML, WordPress, Next.js, React, Vue and single-page apps. Dashboard basics Use periods, traffic modes, filters, saved segments, widgets and custom dashboards. Troubleshooting Fix missing pageviews, wrong domains, blocked scripts and bot traffic.
## Common tasks | Task | Where to go | | --- | --- | | Add a new site | [Add a site](/docs/getting-started/add-site) | | Confirm that tracking works | [Verify installation](/docs/getting-started/verify-installation) | | Track button clicks without custom code | [No-code events](/docs/features/no-code-events) | | Exclude private or internal pages | [Path exclusions](/docs/sites/path-exclusions) | | Connect multiple domains to one site | [Site aliases](/docs/sites/site-aliases) | | Use UTM reports | [Campaign analytics](/docs/features/campaign-utm-analytics) | | Build a funnel | [Goals and funnels](/docs/features/goals-and-funnels) | | Export a large CSV | [Exports](/docs/features/exports) | | Configure privacy settings | [Privacy Center](/docs/privacy/privacy-center) | ## Product areas PureStats documentation is organized around the main product surfaces: - **Getting Started** for account setup, installation and first dashboard use. - **Installation Guides** for framework-specific tracker placement. - **Analytics Features** for realtime, filters, campaigns, events, goals, funnels, Search Console, reports and alerts. - **Sites & Privacy** for site settings, aliases, path exclusions, retention, bot filtering and consent behavior. - **Account & Security** for team access, SSO, two-factor authentication and active sessions. - **Reference** for tracker attributes, JavaScript API calls, Events API payloads and troubleshooting. ## Tracking at a glance The required client-side script is `pf.js`: ```html ``` Place it in the `` of every public page you want to measure. PureStats validates the configured domain or alias before accepting events, so random hostnames and exploit-like domain strings are rejected before they enter analytics data. ## Need operational visibility? Site owners can use Site Health, verification states, scheduled reports and alert rules to detect tracking problems early. --- ## Quick start Source: https://purestats.io/docs/getting-started/quick-start.md This guide gets a new site from zero to a verified PureStats dashboard. Try the [interactive demo](/demo) without an account. Its data is fictional and its period, source, campaign and funnel views can be explored safely. ## 1. Create or sign in to your account Use email, Google SSO or GitHub SSO. Email accounts must confirm their email address before they can sign in. After the first login, open the Analytics area and add your first website. ## 2. Add your site Enter the canonical domain, for example `example.com`. Do not include `https://`, paths, query strings or fragments. PureStats automatically adds the matching `www.` or non-`www.` hostname when available. Add app, docs, checkout or other product hostnames later as [site aliases](/docs/sites/site-aliases). PureStats rejects invalid hostnames and stores only domains that can belong to a real website. This keeps exploit payloads and spam hostnames out of the sites list. The timezone initially follows your browser. Confirm it before saving. The **Getting started** tab guides you through platform selection, installation, a received visit and your first goal. ## 3. Install the tracker Add the script to every page you want to track: ```html ``` Use the canonical domain from your PureStats site settings. When aliases are configured, the tracker may run on any allowed domain, but `data-domain` should still identify the canonical site. ## 4. Verify the installation Open your website in a browser, then go to the site setup or Site Health view in PureStats. Verification checks three separate states: - The script tag is present on the page. - The script file loads successfully. - PureStats receives the first pageview hit. ## 5. Create a conversion goal After a real tracking hit is received, create a goal in Getting started. Choose a page such as `/thank-you`, or an event your application already sends. Goals count future matching actions. More goal types and funnels are available in Site Settings. ## 6. Confirm a real conversion Getting started offers templates for a completed signup, a successful contact form or a thank-you page. Send event goals only after the underlying action succeeds. Use [Tracking test mode](/docs/features/tracking-test-mode) to diagnose matching without changing statistics. The final setup step completes only after a real human conversion is received and shows the elapsed time since website creation. ## 7. Read the dashboard After the first hit, open the analytics dashboard and choose a period such as Today, This Week or This Month. The cards show current visitors, visitors, pageviews and views per visit with period-to-date comparison where applicable. Switching reports retains the selected period, comparison, filters and segment. Automatic weekly insights summarize the last completed local calendar week independently of the currently selected dashboard filters. :::tip If you installed the tracker but still see no data, open [Troubleshooting](/docs/reference/troubleshooting). The most common causes are a wrong `data-domain`, an unconfigured non-`www` alias, CSP blocking the script, or an excluded path. ::: --- ## Create an account Source: https://purestats.io/docs/getting-started/create-account.md PureStats supports three sign-up methods: email, Google and GitHub. All methods create a normal PureStats user that can own sites, join teams, receive reports and manage account security. ## Email sign-up 1. Open the sign-up page. 2. Choose **Continue with email**. 3. Enter your name, email address and password. 4. Confirm your email address from the verification email. 5. Sign in after confirmation. Email accounts stay inactive until the email address is verified. This prevents fake accounts and reduces support issues caused by mistyped email addresses. ## Google and GitHub SSO Google and GitHub sign-up redirects you to the provider, then back to PureStats. If the provider returns a verified email address, PureStats can activate the account immediately. If the same email address already exists, PureStats links the provider only when it can safely confirm the identity. If there is a conflict, sign in with the existing method first and connect the provider from your profile. ## Account security checklist After creating an account, open your profile and review: - Two-factor authentication. - Connected Google or GitHub accounts. - Active sessions. - Email report preferences. - Password status for SSO-created accounts. ## After sign-up Once your account is active, add your first site, review your profile settings and connect any SSO providers you want to use for future sign-ins. --- ## Add a site Source: https://purestats.io/docs/getting-started/add-site.md A site represents one analytics property. It has one canonical domain and can have additional allowed domains through site aliases. ## Domain format Enter only the hostname: ```text example.com ``` Do not enter: ```text https://example.com example.com/pricing example.com?utm_source=test *.example.com ``` PureStats normalizes case, removes unsafe input and validates hostnames before the site can be stored. ## Canonical domain The canonical domain is the main identity for the site. It is used in dashboards, filters, export jobs, alerts, report schedules and tracking verification. Use the root domain if your public site lives there. Use a subdomain if that is the actual product surface, for example `app.example.com`. ## Multiple domains PureStats automatically adds the matching `www.` or non-`www.` hostname when it is not already assigned to another site. If your user journey spans additional hostnames, add aliases after creating the site: - `app.example.com` - `checkout.example.com` - `docs.example.com` Aliases let PureStats accept valid traffic for the same site while still rejecting random domains. ## Settings to review after creation Open Site Settings and confirm: - Timezone. - Bot and spam filtering. - IP anonymization. - Path exclusions. - Cross-domain stitching. - Team access. Once the site is created, install the tracker from the setup panel. --- ## Install the tracker Source: https://purestats.io/docs/getting-started/install-tracker.md The PureStats tracker is a small first-party style script served from `https://purestats.io/pf.js`. It records pageviews, custom events, no-code events, campaign parameters and optional cross-domain identifiers. ## Basic script Place this snippet in the `` of every page you want to track: ```html ``` Replace `example.com` with the canonical domain from PureStats. Consent mode, Do Not Track handling, excluded paths and cross-domain settings are loaded from Site Settings automatically. ## Recommended placement Use the document `` so the tracker initializes early without blocking rendering. The `defer` attribute lets the browser download the script while parsing the page and execute it after the document is ready. ## Optional attributes The tracker supports additional attributes for advanced setups, but they are not required for normal installations: ```html ``` Most sites only need `data-domain`. Use dashboard settings instead of hard-coded options when the setting should be managed centrally or by non-developers. ## First-party proxy If you want the browser to load PureStats through your own domain, proxy the tracker and event endpoints from your site: ```html ``` The proxy must forward `/pf.min.js`, optional modules such as `/pf-vitals.min.js`, `/pf-experiments.min.js` and `/pf-events.min.js`, `/api/event` and `/api/tracker-config` to PureStats. See [First-party proxy](/docs/installation/first-party-proxy) for Nginx, Apache and Cloudflare examples. ## Single-page apps For React, Vue, Next.js client transitions or other SPA routing, PureStats listens for history changes and can record virtual pageviews. See [SPA routing](/docs/installation/spa-routing) for framework notes. ## Verify the first hit After deployment, open your site in a normal browser session and check Site Health. If your own visit is filtered out because of bot, spam, consent or path rules, use another browser profile or temporarily adjust the setting. --- ## Verify installation Source: https://purestats.io/docs/getting-started/verify-installation.md PureStats verification is split into separate checks so you can see exactly where the setup fails. ## Verification states | State | Meaning | | --- | --- | | Script found | The configured page contains a PureStats script tag. | | Script loads | The browser can load `pf.js` from PureStats. | | First hit received | PureStats accepted a pageview for the site or one of its aliases. | All three states must pass before the site should be considered fully installed. ## How to run a manual check 1. Open your site in a normal browser. 2. Disable browser extensions that block analytics scripts. 3. Confirm the script exists in page source. 4. Open the browser Network panel and look for `pf.js`. 5. Reload the page and check whether `/api/event` returns a successful response. 6. Return to PureStats and refresh Site Health. ## Common failure reasons - `data-domain` does not match the canonical site or alias. - The website has a Content Security Policy that blocks `https://purestats.io`. - The current path is excluded in Site Settings. - Consent mode is enabled and no consent was granted. - A bot/spam filter suppresses the visit. ## Verification and aliases PureStats automatically pairs `example.com` and `www.example.com` when the matching hostname is available. For other hostnames such as `app.example.com`, `docs.example.com` or checkout domains, add the hostname as an alias before installing the tracker there. Without an allowed alias, PureStats rejects the event because the hostname is not allowed for that site. :::info Verification checks are intentionally stricter than dashboard filters. A hit must first be accepted by the tracking API before dashboard filters can include or exclude it. ::: --- ## Dashboard basics Source: https://purestats.io/docs/getting-started/dashboard-basics.md The dashboard gives you the current state of a site without requiring a custom report before you can answer basic questions. ## Periods Use the period selector to switch between Today, Yesterday, This Week, Last Week, This Month, Last Month, This Year and Last Year. Comparisons are period-to-date where relevant, so a partial current week is compared with the same elapsed portion of the previous week. ## Traffic mode PureStats can separate normal traffic from bot, spam and AI traffic when filters are enabled. Use the traffic selector to inspect all traffic or focus on clean visitor traffic. ## Summary cards The top cards show: - Current visitors. - Visitors. - Pageviews. - Views per visit. Visitors and pageviews include percentage comparison when the previous period has enough data. Views per visit is rounded without unnecessary trailing decimals. ## Charts and tables The dashboard includes trends, top pages, sources, campaigns, locations, devices, browsers, operating systems and goal performance. Tables can be filtered by clicking rows or using the filter modal. ## Filters Filters let you narrow the dashboard by source, campaign, landing page, pathname, country, browser, operating system, traffic mode and UTM values. Save filters as segments when you reuse them often. ## Custom dashboards Saved dashboards let each user choose which widgets appear first. This is useful when a marketer wants campaigns and goals, while an operator wants realtime, alerts and site health. --- ## Plain HTML Source: https://purestats.io/docs/installation/plain-html.md Static websites only need the standard script tag. ## Add the script Place the snippet before the closing `` tag: ```html Example ... ``` Deploy the change and open the page in a browser. PureStats should receive a pageview after the script loads. ## Multiple pages Add the script to your shared layout, include, partial or template so it appears on every public page. Avoid manually adding it page by page unless the site is very small. ## Excluded pages If the script is part of a shared layout but some paths should not be tracked, use [Path exclusions](/docs/sites/path-exclusions). Typical exclusions are: - `/admin` - `/admin/*` - `/preview/*` - `/account/billing` ## Content Security Policy If your site uses CSP, allow the tracker source and event endpoint: ```http script-src 'self' https://purestats.io; connect-src 'self' https://purestats.io; ``` Keep the policy as strict as possible while allowing `pf.js` and its network request. --- ## WordPress Source: https://purestats.io/docs/installation/wordpress.md WordPress sites can install PureStats through a theme file, a header/footer injection plugin or a custom site plugin. ## Header injection plugin The simplest option is a reputable header injection plugin: 1. Install a plugin that can add scripts to the site header. 2. Paste the PureStats script. 3. Save and clear page caches. 4. Open the public site and verify the first hit. ```html ``` ## Theme template If you control the theme, place the script in `header.php` before `wp_head()` or immediately after it. Prefer a child theme so future theme updates do not overwrite the change. ```php ``` ## Custom plugin For teams with deployment workflows, create a tiny plugin that hooks into `wp_head`: ```php add_action('wp_head', function () { echo ''; }); ``` ## Cache and optimization plugins After installing, clear all caches. If an optimization plugin combines or delays scripts, exclude `https://purestats.io/pf.js` from aggressive rewriting until tracking is verified. ## Private pages PureStats normally tracks public pages only because the script is placed in the public theme. If your setup injects it into private WordPress pages too, add `/wp-admin/*` to path exclusions. --- ## Shopify Source: https://purestats.io/docs/installation/shopify.md Shopify stores can install PureStats in the theme layout so the tracker is loaded on storefront pages. ## Add the tracker to theme.liquid 1. Open your Shopify admin. 2. Go to **Online Store** -> **Themes**. 3. Choose your active theme and open **Edit code**. 4. Open `layout/theme.liquid`. 5. Paste the PureStats script before the closing `` tag. 6. Save the theme and open your storefront to send the first pageview. ```html ``` Replace `example.com` with the domain configured in PureStats. ## Theme updates Theme updates can overwrite custom template edits. If your theme supports custom code injection, prefer that option. Otherwise, note the change in your deployment checklist so the tracker is re-added after theme updates. ## Checkout and additional domains Shopify checkout can run on a different hostname depending on your plan and configuration. If you track across multiple hostnames, add every allowed hostname in Site Settings -> Domains before enabling cross-domain stitching. ## Avoid tracking admin and preview pages The script should be installed only in the public storefront theme. If a theme app or custom setup injects it into preview, account or internal pages, add path exclusions in PureStats. Common exclusions: - `/admin` - `/admin/*` - `/account` - `/account/*` - `/checkout/*` ## Verify installation After saving the theme, open a public product, collection or homepage URL in a normal browser window. Then click **Verify Installation** in PureStats. If the script loads but no hit is received, check consent apps, theme optimization apps and Content Security Policy settings. --- ## First-party proxy Source: https://purestats.io/docs/installation/first-party-proxy.md A first-party proxy lets your website load PureStats from your own domain instead of loading the script directly from `purestats.io`. Use this when you want the browser Network panel, CSP and deployment checks to use your own origin, for example: ```html ``` Keep `data-domain` set to the canonical domain in PureStats. The `data-api` attribute tells the tracker to send events to your proxy. PureStats automatically derives the config endpoint from it and requests `/api/tracker-config` on the same origin. ## Proxy paths Proxy these paths from your website to PureStats: | Your path | Upstream | Cache | | --- | --- | --- | | `/pf.min.js` | `https://purestats.io/pf.min.js` | Yes, about 1 hour | | `/pf-test.min.js` | `https://purestats.io/pf-test.min.js` | Yes, about 1 hour | | `/pf-vitals.min.js` | `https://purestats.io/pf-vitals.min.js` | Yes, about 1 hour | | `/pf-experiments.min.js` | `https://purestats.io/pf-experiments.min.js` | Yes, about 1 hour | | `/pf-events.min.js` | `https://purestats.io/pf-events.min.js` | Yes, about 1 hour | | `/pf-search.min.js` | `https://purestats.io/pf-search.min.js` | Yes, about 1 hour | | `/pf-replay.min.js` | `https://purestats.io/pf-replay.min.js` | Yes, about 1 hour | | `/api/event` | `https://purestats.io/api/event` | No | | `/api/tracker-config` | `https://purestats.io/api/tracker-config` | No, or very short | | `/api/replay/chunks` | `https://purestats.io/api/replay/chunks` | No | Do not cache `/api/event` or `/api/replay/chunks`. They receive analytics and optional replay payloads. ## Client IP trust Register your proxy's egress IPs or CIDRs in the site's proxy settings before accepting forwarded client IPs. Derive the client address from the trusted connection, never from visitor-supplied forwarding headers. If Nginx or Apache sits behind another edge, configure its real-IP module with only that edge's trusted addresses first. The Nginx and Apache examples strip incoming forwarding and PureStats signature headers and set a server-derived client IP. They do not generate signatures. HMAC signing is recommended; if `require_signature` is enabled, add a server-side signing layer or use the signed Cloudflare Worker below. Never put a signing key in browser code. ## Nginx Add the cache zone in the `http` block: ```nginx proxy_cache_path /var/cache/nginx/purestats levels=1:2 keys_zone=purestats_tracker:10m max_size=50m inactive=24h use_temp_path=off; ``` Add the locations to your site server block: The static upstream below intentionally has no URI suffix or variables. Nginx preserves the original path and query string and resolves `purestats.io` with the system resolver at startup or reload; no runtime `resolver` directive is needed. Reload Nginx when upstream DNS changes. Adjust the trusted CA bundle path for your distribution, without disabling certificate verification. ```nginx location ~ ^/pf(?:-events|-vitals|-experiments|-search|-replay|-test)?\.min\.js$ { proxy_pass https://purestats.io; proxy_ssl_server_name on; proxy_ssl_verify on; proxy_ssl_verify_depth 3; proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; proxy_set_header Host purestats.io; proxy_set_header Cookie ""; proxy_set_header Authorization ""; proxy_cache purestats_tracker; proxy_cache_valid 200 1h; add_header Cache-Control "public, max-age=3600" always; } location ~ ^/api/(?:event|tracker-config|replay/chunks)$ { proxy_pass https://purestats.io; proxy_ssl_server_name on; proxy_ssl_verify on; proxy_ssl_verify_depth 3; proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; proxy_set_header Host purestats.io; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Real-IP $remote_addr; proxy_set_header CF-Connecting-IP ""; proxy_set_header Forwarded ""; proxy_set_header X-PureStats-Proxy-Domain ""; proxy_set_header X-PureStats-Proxy-Client-IP ""; proxy_set_header X-PureStats-Proxy-Timestamp ""; proxy_set_header X-PureStats-Proxy-Signature ""; proxy_set_header Cookie ""; proxy_set_header Authorization ""; proxy_cache off; } ``` ## Apache Enable `mod_proxy`, `mod_proxy_http`, `mod_ssl` and `mod_headers`, then add: ```apache SSLProxyEngine On ProxyPreserveHost Off ProxyPass "/pf.min.js" "https://purestats.io/pf.min.js" retry=0 ProxyPassReverse "/pf.min.js" "https://purestats.io/pf.min.js" ProxyPass "/pf-test.min.js" "https://purestats.io/pf-test.min.js" retry=0 ProxyPassReverse "/pf-test.min.js" "https://purestats.io/pf-test.min.js" ProxyPass "/pf-vitals.min.js" "https://purestats.io/pf-vitals.min.js" retry=0 ProxyPassReverse "/pf-vitals.min.js" "https://purestats.io/pf-vitals.min.js" ProxyPass "/pf-experiments.min.js" "https://purestats.io/pf-experiments.min.js" retry=0 ProxyPassReverse "/pf-experiments.min.js" "https://purestats.io/pf-experiments.min.js" ProxyPass "/pf-events.min.js" "https://purestats.io/pf-events.min.js" retry=0 ProxyPassReverse "/pf-events.min.js" "https://purestats.io/pf-events.min.js" ProxyPass "/pf-search.min.js" "https://purestats.io/pf-search.min.js" retry=0 ProxyPassReverse "/pf-search.min.js" "https://purestats.io/pf-search.min.js" ProxyPass "/pf-replay.min.js" "https://purestats.io/pf-replay.min.js" retry=0 ProxyPassReverse "/pf-replay.min.js" "https://purestats.io/pf-replay.min.js" ProxyPass "/api/event" "https://purestats.io/api/event" retry=0 ProxyPassReverse "/api/event" "https://purestats.io/api/event" ProxyPass "/api/tracker-config" "https://purestats.io/api/tracker-config" retry=0 ProxyPassReverse "/api/tracker-config" "https://purestats.io/api/tracker-config" ProxyPass "/api/replay/chunks" "https://purestats.io/api/replay/chunks" retry=0 ProxyPassReverse "/api/replay/chunks" "https://purestats.io/api/replay/chunks" RequestHeader unset Forwarded RequestHeader unset X-Forwarded-For RequestHeader unset X-Real-IP RequestHeader unset CF-Connecting-IP RequestHeader unset X-PureStats-Proxy-Domain RequestHeader unset X-PureStats-Proxy-Client-IP RequestHeader unset X-PureStats-Proxy-Timestamp RequestHeader unset X-PureStats-Proxy-Signature RequestHeader unset Cookie RequestHeader unset Authorization RequestHeader set X-Forwarded-For "expr=%{REMOTE_ADDR}" RequestHeader set X-Real-IP "expr=%{REMOTE_ADDR}" Header set Cache-Control "public, max-age=3600" ``` ## Cloudflare Worker Create a Worker and route it to your domain for `/pf.min.js`, `/pf-test.min.js`, `/pf-vitals.min.js`, `/pf-experiments.min.js`, `/pf-events.min.js`, `/pf-search.min.js`, `/pf-replay.min.js`, `/api/event`, `/api/tracker-config` and `/api/replay/chunks`. Set `domain` below to your canonical PureStats site domain. Rotate a signing key through the site's approved proxy settings, store the newly generated value only in the `PURESTATS_PROXY_SIGNING_KEY` Worker secret binding, and register the Worker's egress IPs or CIDRs. Enable `require_signature`. The key is not an OAuth credential and must never appear in the deployed script, a public environment binding or browser code. Cloudflare supplies `CF-Connecting-IP` at its trusted edge. This example discards visitor-supplied forwarding and PureStats headers, signs the exact raw POST body with HMAC-SHA256, strips browser cookies and authorization, and caches successful JavaScript only in the edge Cache API. API responses are never cached. ```js const allowed = ['/pf.min.js', '/pf-test.min.js', '/pf-vitals.min.js', '/pf-experiments.min.js', '/pf-events.min.js', '/pf-search.min.js', '/pf-replay.min.js', '/api/event', '/api/tracker-config', '/api/replay/chunks']; const domain = 'example.com'; const hex = (buffer) => Array.from(new Uint8Array(buffer), (byte) => byte.toString(16).padStart(2, '0')).join(''); export default { async fetch(request, env, ctx) { const url = new URL(request.url); if (!allowed.includes(url.pathname)) return fetch(request); const isScript = request.method === 'GET' && url.pathname.endsWith('.min.js'); const cacheKey = new Request(url.toString(), { method: 'GET' }); if (isScript) { const hit = await caches.default.match(cacheKey); if (hit) return hit; } const clientIp = request.headers.get('CF-Connecting-IP'); const signingKey = env.PURESTATS_PROXY_SIGNING_KEY; if (!clientIp || typeof signingKey !== 'string' || signingKey.length < 32) { return new Response('Proxy signing is not configured', { status: 503 }); } const headers = new Headers(request.headers); for (const name of Array.from(headers.keys())) { if (name.startsWith('x-purestats-proxy-') || name.startsWith('x-forwarded-') || ['forwarded', 'x-real-ip', 'cookie', 'authorization', 'host'].includes(name)) headers.delete(name); } const body = ['GET', 'HEAD'].includes(request.method) ? null : await request.arrayBuffer(); const timestamp = String(Math.floor(Date.now() / 1000)); const bodyHash = hex(await crypto.subtle.digest('SHA-256', body ?? new Uint8Array())); const message = [domain.toLowerCase().trim(), clientIp.trim(), timestamp, bodyHash].join('\n'); const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(signingKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']); const signature = hex(await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(message))); headers.set('X-PureStats-Proxy-Domain', domain.toLowerCase().trim()); headers.set('X-PureStats-Proxy-Client-IP', clientIp.trim()); headers.set('X-PureStats-Proxy-Timestamp', timestamp); headers.set('X-PureStats-Proxy-Signature', signature); headers.set('X-Forwarded-Host', url.hostname); headers.set('X-Forwarded-Proto', url.protocol.slice(0, -1)); headers.set('X-Forwarded-For', clientIp.trim()); const upstream = new URL(url.pathname + url.search, 'https://purestats.io'); const response = await fetch(new Request(upstream.toString(), { method: request.method, headers, body, redirect: 'manual' })); const result = new Response(response.body, response); result.headers.delete('Set-Cookie'); if (isScript && result.status === 200) { result.headers.set('Cache-Control', 'public, max-age=3600'); ctx.waitUntil(caches.default.put(cacheKey, result.clone())); } else result.headers.set('Cache-Control', 'no-store'); return result; } }; ``` ## Content Security Policy If all PureStats requests go through your domain, a strict CSP can allow only your own origin: ```text script-src 'self'; connect-src 'self'; img-src 'self' data:; ``` If you keep the normal direct script and only proxy events, also allow `https://purestats.io` in `script-src`. ## Verify 1. Open `https://example.com/pf.min.js` and confirm it returns JavaScript. 2. Install the first-party script snippet. 3. Open your site and check the browser Network panel. 4. Confirm `/pf.min.js`, enabled optional modules, `/api/tracker-config`, `/api/event` and `/api/replay/chunks` are requested on your own domain. 5. Open PureStats Site Health and verify the first hit. ## Troubleshooting - If the script loads but no pageviews appear, check that `/api/event` forwards POST bodies. - If settings do not apply, check that `/api/tracker-config?domain=example.com` reaches PureStats. - If CSP blocks requests, allow your own origin in `script-src` and `connect-src`. - If the proxy returns 404, make sure the route is configured before your app fallback route. - If the proxy returns 405, make sure POST is allowed for `/api/event`. - If the dashboard shows a domain mismatch, keep `data-domain` set to the canonical PureStats site domain and add every real hostname in Site Settings. --- ## Next.js Source: https://purestats.io/docs/installation/nextjs.md Next.js installations should load `pf.js` once in the root layout. ## App Router Add the script in `app/layout.tsx` or `app/layout.jsx`: ```tsx import Script from 'next/script'; export default function RootLayout({ children }) { return ( {children} ``` The tracker will capture the initial pageview and listen for history changes created by common routers. ## React Router For most React Router apps, no extra code is required. If your app uses memory routing or custom navigation that does not update the browser URL, manually trigger a pageview: ```js window.purestats?.trackPageview(); ``` Call it after the visible route changes. ## Custom events Use JavaScript events for product interactions that are not pageviews: ```js window.purestats?.track('Signup Started', { plan: 'pro', location: 'pricing' }); ``` For simple buttons or forms, no-code events may be easier to maintain. ## Development mode Avoid tracking local development unless you deliberately add `localhost` as an alias for testing. Production builds should use the real configured domain. --- ## Vue Source: https://purestats.io/docs/installation/vue.md Vue apps can load PureStats once in the application HTML file. ## Basic setup Add the tracker to `index.html`: ```html
``` This records the first pageview and listens for History API navigation. ## Vue Router If your router uses standard browser history, PureStats should track route changes automatically. For custom routing behavior, trigger a manual pageview after navigation: ```js router.afterEach(() => { window.purestats?.trackPageview(); }); ``` Use this only if automatic SPA tracking does not capture route changes. Duplicate pageviews can occur if both mechanisms fire. ## Custom events Track meaningful interactions: ```js window.purestats?.track('Trial Started', { source: 'onboarding' }); ``` Event names should be stable. Avoid encoding dynamic values in event names; put them in properties instead. ## Excluding internal routes If the app includes private routes such as `/admin`, configure path exclusions in PureStats. This is easier to audit than adding conditional script loading across route components. --- ## SPA routing Source: https://purestats.io/docs/installation/spa-routing.md Single-page applications change the URL without a full document reload. PureStats handles common History API route changes automatically, but custom routers may need manual pageview calls. ## Automatic tracking The tracker observes: - Initial page load. - `history.pushState`. - `history.replaceState`. - Browser back and forward navigation. This covers most React Router, Vue Router and Next.js client transitions. ## Manual tracking If your app changes visible screens without changing the URL, call: ```js window.purestats?.trackPageview('/billing/checkout'); ``` Pass a path when the browser URL does not reflect the view. If you omit the argument, PureStats uses `window.location.pathname`. ## Avoid duplicate pageviews Do not call `trackPageview()` after every route change unless automatic tracking fails. Check the Network panel: one route change should produce one accepted pageview event. ## Campaign parameters UTM parameters are captured on landing pages. If a SPA removes UTM parameters immediately after load, make sure the tracker runs before the URL cleanup. ## Cross-domain flows When a visitor moves from `www.example.com` to `checkout.example.com`, configure aliases and enable cross-domain stitching if you need the session to continue across hostnames. --- ## Realtime analytics Source: https://purestats.io/docs/features/realtime.md Realtime analytics shows what is happening on a site right now. Use it when you deploy a campaign, verify a tracking change, monitor a launch or investigate an incident. ## What realtime shows The realtime view focuses on active traffic: - Current visitors. - Active pages. - Recent pageviews. - Recent custom events. - Referrers and campaigns for live visitors. - Device and location breakdowns when available. ## Current visitors Current visitors are calculated from recent sessions with activity inside the realtime window. A visitor disappears from the count after inactivity. If a pageview is accepted but current visitors remains zero, check that the session write succeeded and that the dashboard traffic mode is not excluding that visitor. ## Filtering realtime data Realtime respects the selected site and traffic mode. If you choose clean traffic only, bot, spam and AI traffic may be excluded. Switch to all traffic when debugging a missing hit. ## Good uses - Confirm a new tracker deployment. - Watch campaign traffic after sending an email. - See whether a landing page receives traffic. - Monitor active paths during a release. ## Limitations Realtime is intentionally short-lived. Use dashboard periods, exports or reports for historical analysis. If you need to preserve a live incident, export the related time range afterward. --- ## Automatic weekly insights Source: https://purestats.io/docs/features/weekly-insights.md The overview summarizes the last completed local calendar week, independently of the dashboard's active filters. Open its details for the largest changes by landing page and source, and save useful observations to the action board. Comparisons need at least 20 human visits in each period, tracking history covering both periods and no known tracking gaps. Imported aggregates are excluded. Stitched visits are counted once; changes describe observations, not their cause. Conversion rates count distinct converting human visits from the same visit cohort, rather than conversion-event totals. Details show up to ten active goals. Goals created after the preceding period began are not compared. Missing comparison coverage withholds rates and changes. ## Receive the summary by email Create an enabled weekly schedule under **Site Settings → Reports**, choose the weekly template and check its recipient. The report includes automatically calculated insights for its completed period. A test or manually queued report can verify the mail workflow; the delivery history distinguishes pending, sent, suppressed and failed messages. Provider acceptance does not by itself prove inbox delivery. --- ## Actions and observed results Source: https://purestats.io/docs/features/actions.md Open **Analytics → More → Actions**. Owners and administrators can create, edit, assign and delete actions. Team members can read them. Tasks remain private even when a website has a public analytics dashboard. Use **Save as action** on a weekly insight or SEO opportunity to retain its context. Saving the same source again opens no duplicate and preserves the team's existing work. Assign an active member of the same website and choose Planned, In progress, Done or Dismissed. Record the actual date when the change goes live. This also creates a chart annotation. Clearing the date or deleting the action removes its associated annotation. ## Before and after PureStats shows two equal 14-day windows, excluding the change day. Visits and conversions follow the website timezone. A landing-page action limits visits to that entry page and conversions to those visits. Search clicks follow provider dates and are shown only when connected providers have successful coverage for both windows. Untracked property hostnames are excluded. Interpretation waits for the full period, adequate website history and no known tracking gaps. Observed changes do not prove that the action caused them. Consider seasonality, other changes and sample size before drawing conclusions. The board lists the 100 most recently updated actions. --- ## Tracking test mode Source: https://purestats.io/docs/features/tracking-test-mode.md From **Getting started** or Site Settings, open **Tracking test mode**. A website owner or administrator can start a private session lasting 15 minutes. Open its website link in a separate tab, navigate and perform the action you want to diagnose. The current tracker detects the token in the URL fragment, removes the fragment and keeps the session in that tab's session storage. The token is stored as a hash on the server. Do not share the link. First-party proxies must also forward `/pf-test.min.js`. ## What you can verify - Whether requests arrive for the configured domain. - Consent, Do Not Track and excluded-path decisions. - Page paths and event names, including delegated no-code events. - Which active goals match each accepted diagnostic event. Diagnostic acceptance validates these setup rules. It does not predict production traffic classification, deduplication or rate limits. Only diagnostic event names, paths, decisions and matching goal names are stored, separately from analytics. Query strings, referrers and client identities are not retained. A session accepts at most 500 events, and the page shows the latest 50. Test events never create visitors, conversions, rollups, integrations or tracking-health evidence. A revoked or expired token is rejected and never falls through to normal ingestion. End the session and close its tab when finished: that tab stays isolated after expiry. A new session replaces your previous session for that website; old diagnostic records are cleared on replacement or explicit end, and expired sessions are pruned when a new test starts. If no events arrive, confirm that the current tracker and optional test module load, your proxy forwards both, and the browser's blocker or Content Security Policy allows them. A test session cannot validate a missing script. --- ## Retention analytics Source: https://purestats.io/docs/features/retention-analytics.md # Retention analytics Retention analytics explains whether people return after their first visit. Open **Analytics → More → Retention** to compare new and returning visitors, inspect visit frequency, review daily or weekly cohorts, and monitor DAU, WAU, MAU, and DAU/MAU stickiness. PureStats uses the strongest privacy-safe identity available for a site. An optional pseudonymous user identity takes precedence, followed by the first-party visitor identity used for cross-domain tracking, and finally the regular anonymous daily visitor identity. Raw user IDs are never stored. ## Identify signed-in users Call `identify` before the initial pageview when your application already knows a stable internal user ID: ```html ``` The identifier is transmitted to PureStats and immediately transformed with a site-specific HMAC. It is not written to logs or analytics tables. Do not send an email address, name, phone number, or any other directly identifying value. Use an opaque internal ID. Reset the identity on logout or account switching: ```js purestats('resetIdentity') ``` You can also use `purestats.identify(id)` and `purestats.resetIdentity()` after the tracker has loaded. ## Historical coverage Existing pageviews are backfilled into visitor profiles and daily activity. The coverage notice on the Retention page states the first and last included date and whether data came from the native tracker, a backfill, or both. Retention reports never create synthetic sessions or modify historical pageviews. --- ## Core Web Vitals and real user monitoring Source: https://purestats.io/docs/features/core-web-vitals.md # Core Web Vitals and real user monitoring PureStats measures page performance from real visits with Google's official `web-vitals` library. Open **More → Performance** in a site's analytics navigation to review field measurements for LCP, CLS, INP, FCP and TTFB. ## What is collected Each sample contains the metric name, value, rating, page path, navigation type and the already detected device and browser. Reports show P50, P75 and P95 values. P75 is the primary value used for the Google rating. PureStats does not collect DOM content, resource URLs or performance traces for Core Web Vitals. Measurements follow the site's existing consent mode, Do Not Track choice, path exclusions and bot-traffic rules. ## Enable or disable collection Web Vitals are enabled by default for new and existing sites. A site owner or manager can change this under **Site settings → Privacy → Data collection**. The setting is delivered through the tracker configuration, so the standard tracking snippet does not need another data attribute. ## Optional tracker module The core tracker remains under 7 KB compressed. After the server setting allows collection, it loads `pf-vitals.min.js` as a separate optional module. When a first-party proxy is used, proxy this file in addition to the core tracker: ```text /pf.min.js → https://purestats.io/pf.min.js /pf-vitals.min.js → https://purestats.io/pf-vitals.min.js /api/tracker-config → https://purestats.io/api/tracker-config /api/event → https://purestats.io/api/event ``` Both JavaScript files can be cached for one hour. API responses must not be cached. ## Reading the report - **P50** describes the median visit. - **P75** is the primary field-quality threshold. - **P95** surfaces severe slow-tail experiences. - **Pages needing attention** lists page and metric combinations outside the good threshold. - Device, browser and navigation tables help isolate environment-specific regressions. New samples appear immediately in the report. Daily percentile rollups are refreshed hourly for operational and long-term reporting. --- ## Historical analytics imports Source: https://purestats.io/docs/features/historical-imports.md # Historical analytics imports PureStats can bring historical aggregate analytics into a site from Google Analytics 4, Plausible, Matomo or a CSV file. Imports are useful when you want charts to retain context from before the PureStats tracker was installed. ## How imported data is handled Imported values are stored separately from native PureStats pageviews, sessions, events and visitor profiles. PureStats never turns provider aggregates into artificial people or journeys. The standard Overview uses imported values only for dates before the first native PureStats hit. If providers overlap, PureStats keeps both sources available but selects only the latest imported source for each day. It never silently adds overlapping providers together. Imported aggregates are not applied to detailed traffic-mode or dimension-filter views because upstream providers cannot reproduce PureStats bot classification and visitor-level filters. ## Start an import 1. Open **Site settings** and select **Historical imports**. 2. Choose GA4, Plausible, Matomo or CSV. 3. Select the historical date range. 4. Enter the read-only provider credential and property identifier, or upload a CSV and map its columns. 5. Select **Connect and import**. Credentials are encrypted before storage and are never returned to the browser. CSV files are private and removed after a successful import. ## Resume or remove data Provider imports save a checkpoint after every completed API page. A failed or cancelled job can continue from that checkpoint. Deleting a connection removes its credentials, jobs and imported aggregates, but does not modify native PureStats data. ## CSV format The first row must contain headers. A date column is required and accepts `YYYY-MM-DD`, `YYYY/MM/DD`, `MM/DD/YYYY` or `DD.MM.YYYY`. Metric columns can include visitors, visits, pageviews, bounces, duration, events, conversions and revenue. Optional dimension columns include page, source, country, device, browser and operating system. CSV jobs accept up to 25 MB and 250,000 data rows. Split larger exports into separate date ranges. --- ## Experiments and A/B tests Source: https://purestats.io/docs/features/experiments.md # Experiments and A/B tests PureStats can run first-party A/B tests without sending visitor assignments to a third-party feature flag provider. Open **Analytics → More → Experiments** to define variants, weights and the conversion outcome. ## Create an experiment 1. Choose a stable experiment key such as `homepage_headline`. 2. Add two or more variants whose weights total 100%. 3. Mark exactly one variant as the control. 4. Select a goal, custom event or revenue event as the outcome. 5. Start the experiment when the implementation is live. Variants and outcome definitions are locked while an experiment is running. Pause it before changing the implementation, or complete it to preserve the final result. ## Assign a variant The optional experiment module loads automatically whenever the site has a running experiment: ```js window.addEventListener('purestats:experiments-ready', () => { const variant = purestats.assignExperiment('homepage_headline'); renderHeadline(variant); purestats.trackExposure('homepage_headline', variant); }); ``` Assignment is deterministic and respects the configured weights. Call `trackExposure()` only when the visitor can actually see the tested experience. Repeat calls in one session are suppressed in the browser, while the server also guarantees one exposure per experiment subject. ## Read the result Each variant shows exposures, unique converters, conversion rate, lift from control and a Wilson 95% confidence interval. PureStats applies a Holm correction when several variants are compared with the control. A winner can appear only after the experiment has run for at least seven days, every variant has at least 100 exposures, and the adjusted result reaches 95% confidence. PureStats never stops an experiment automatically. Device, country and traffic-source segments use the same safeguards. Segment results are diagnostic; avoid choosing a winner from a small subgroup after inspecting many segments. ## Privacy and proxies The module follows the site's consent, Do Not Track, path exclusion and traffic classification settings. It stores no raw account identity. First-party proxy installations must also forward and cache `/pf-experiments.min.js`. --- ## E-commerce analytics Source: https://purestats.io/docs/features/ecommerce-analytics.md PureStats understands the standard commerce events `view_item`, `add_to_cart`, `begin_checkout`, `purchase` and `refund`. Commerce reporting is optional and uses the same consent, path exclusion and traffic classification rules as other analytics events. ## Set the reporting currency Open **Site Settings → General** and select the currency used for the main commerce report. Orders in other currencies remain visible as separate totals. PureStats never combines currencies without an explicit exchange rate. ## Track a product view ```js window.purestats?.track('view_item', { items: [{ item_id: 'product-42', sku: 'PS-42', name: 'Analytics Handbook', category: 'Books', price: 29.00, quantity: 1 }] }); ``` Use the same `items` structure for `add_to_cart` and `begin_checkout`. ## Track a purchase ```js window.purestats?.track('purchase', { order_id: 'order-10042', value: 58.00, currency: 'EUR', tax: 9.26, shipping: 0, discount: 0, items: [{ item_id: 'product-42', sku: 'PS-42', name: 'Analytics Handbook', category: 'Books', price: 29.00, quantity: 2 }] }); ``` `order_id` makes purchases idempotent. Sending the same order again updates the normalized order instead of creating a second order. PureStats encrypts the reference and only shows a non-reversible fingerprint in analytics. ## Track a refund ```js window.purestats?.track('refund', { order_id: 'order-10042', refund_id: 'refund-204', value: 29.00, currency: 'EUR' }); ``` The original purchase must exist before its refund. `refund_id` provides retry-safe deduplication. ## Privacy and validation Commerce payloads accept product and financial fields only. Do not send names, email addresses, phone numbers, delivery addresses or payment details. Unknown commerce fields are rejected rather than stored. The Commerce report includes gross and net revenue, orders, average order value, refunds, checkout progression, products and acquisition attribution. Commerce can also be included in scheduled reports, CSV exports, visitor sessions, experiments and revenue-drop alerts. --- ## Custom dimensions and Explore Source: https://purestats.io/docs/features/custom-dimensions.md Custom dimensions turn selected event and user properties into indexed reporting fields. PureStats keeps the original event properties compatible, but only properties you explicitly define are indexed for filters and reports. ## Create a dimension Open **Site settings → Custom dimensions**, then provide a label, property key, scope and value type. - **Event** stores the value for the pageview or event carrying the property. - **Session** applies the latest supplied value to the current session. - **Visitor** uses properties passed to `identify()` and follows the pseudonymous visitor identity. Supported value types are string, number, boolean and date. Existing event properties are backfilled in the background and the setup screen shows the backfill state. PureStats rejects property keys commonly associated with personal data, including names, email addresses, phone numbers, postal addresses and IP addresses. Do not place personal data in values either. ## Send properties Event properties use the normal event API: ```js purestats('Signup Completed', { account_type: 'customer', seats: 12 }) ``` Visitor properties are sent with an opaque user ID: ```js purestats.identify('internal-user-42', { account_type: 'customer', trial_active: false }) ``` ## Filter and explore Configured dimensions appear in the common analytics filter dialog. They work with overview reports, saved segments, custom dashboards, funnels, exports and the Stats API. Open **Analytics → More → Explore** to combine a metric, dimension, period, comparison and filters. Saved reports remain private to your account and site. The Stats API endpoint `GET /api/stats/custom-dimensions` returns active definitions and their most common values for a selected period. --- ## Internal Site Search Analytics Source: https://purestats.io/docs/features/site-search.md # Internal Site Search Analytics PureStats can show what visitors search for on your website, which searches return no results, which results visitors select and whether a search session later converts. Enable **Site Settings → Site search** first. Explicit tracking is recommended because it lets your application provide an accurate result count: ```js purestats.trackSiteSearch('analytics dashboard', 12) purestats.trackSiteSearchClick('analytics dashboard', '/features/analytics') ``` The optional `pf-search.min.js` module is loaded from the same origin as the main tracker. Existing snippets do not need additional attributes. ## URL detection Automatic URL detection is disabled by default. When enabled, PureStats reads only the query parameters configured for the site, such as `q`, `query`, `search` or `s`. Do not enable it when search URLs can contain names, email addresses or other personal information. ## Privacy safeguards Search terms are limited to 200 characters and screened for email addresses, phone numbers and IP addresses before storage. Queries containing only sensitive data are discarded. Site search follows the site's consent mode, Do Not Track preference, excluded paths and traffic classification. Search activity is available in **Analytics → More → Site Search**, CSV exports, visitor journeys and the Stats API at `GET /api/stats/site-search`. Enabling Site Search also creates the event-scoped **Site search term** custom dimension. This makes the screened `search_query` value available to segments, funnels, Explore reports and other custom-dimension filters without storing a second unscreened value. --- ## Data destinations Source: https://purestats.io/docs/features/data-destinations.md # Data destinations Data destinations copy native PureStats pageviews and custom events to infrastructure you control. Open **Site settings → Destinations** to configure a signed webhook, S3-compatible object storage, Google BigQuery or ClickHouse. ## Delivery guarantees PureStats uses a transactional outbox and delivers each event at least once. Every envelope contains a stable `event_id`; destination consumers should use it as their deduplication key. Failed requests use exponential backoff. A delivery moves to the dead-letter queue after eight failed attempts and can be retried manually. Credentials are encrypted at rest and are never returned to the browser. Pageview envelopes contain pseudonymous visitor and session identifiers, but never the visitor IP address or raw user agent. ## Signed webhooks Webhook requests include: - `X-PureStats-Event-Id` - `X-PureStats-Timestamp` - `X-PureStats-Signature` The signature is `sha256=` followed by the HMAC-SHA256 digest of `.`. Reject stale timestamps and compare signatures in constant time. ## Warehouses and object storage S3 destinations receive one gzip-compressed NDJSON object per event. BigQuery uses the event ID as its stable insert ID. ClickHouse receives `JSONEachRow` requests. All providers use schema version 1 and expose connection status, delivery lag and failures in site settings. ## Backfills Backfills can export native pageviews and events still available inside the site's raw-data retention window. They are checkpointed and can be cancelled without affecting live delivery. Backfills never recreate data already removed by retention. ## Privacy deletions When a matching privacy delete request is processed, PureStats enqueues a `privacy_tombstone`. The tombstone identifies the site and privacy request but does not contain the lookup value. External destinations are separate data copies; you must consume tombstones and enforce deletion and retention there. --- ## Session replay and heatmaps Source: https://purestats.io/docs/features/session-replay.md # Session replay and heatmaps Session replay is an optional site feature for investigating interaction problems that aggregate analytics cannot explain. Recordings are disabled until a site owner configures and verifies S3-compatible storage under **Site settings → Session replay**. PureStats loads the separate `/pf-replay.min.js` module only for enabled sites. The core tracker remains unchanged in size, and recordings follow the site's consent mode, Do Not Track preference, path exclusions and traffic filtering rules. ## Privacy defaults Replay deliberately captures less than the page itself exposes: - All text nodes, form inputs and content-editable values are masked before upload. - Password fields and elements marked with `data-purestats-block`, `data-sensitive` or `.purestats-block` are excluded completely. - URL query strings and fragments are removed from captured element attributes. - Canvas recording, inline images and font collection are disabled. - Server-side sanitization repeats the masking before a chunk is stored. Review your own pages before enabling replay. Block any component that may expose private visual context even after text masking. ## Storage and retention Production recordings are written directly to the S3-compatible endpoint configured for the site. Access credentials are encrypted at rest. Local replay storage is accepted only in development and automated tests. Recordings expire after exactly 30 days. The daily retention job deletes expired objects and database metadata. A configurable storage watermark warns at 80% and pauses new uploads at 100% instead of exceeding the limit. Privacy deletion requests remove matching replay objects as well as analytics records. ## Reviewing recordings Open **More → Session replays** to filter sessions by date and page, inspect click and scroll heatmaps, and launch the sandboxed replay player. Only the site owner and PureStats administrators may list, view or delete recordings. Every list, playback and deletion action is written to the replay access audit log. Replay is a diagnostic tool, not a replacement for aggregate analytics. Keep it disabled when the additional detail is not necessary for a defined investigation. --- ## Chart annotations Source: https://purestats.io/docs/features/chart-annotations.md # Chart annotations Annotations explain why traffic, conversions or performance changed. They appear across Overview, Campaigns, Events, Funnels, Retention and Performance for every member of the site. ## Add an annotation Site owners and managers can select **Add annotation** above an analytics report. Choose a start time, an optional end time, a category and a short title. Use a date range for incidents or campaigns and a single timestamp for releases and product changes. PureStats uses the site's configured timezone when saving and displaying annotation times. Automatically generated import boundaries and known data gaps are read-only so their operational history stays intact. ## Deployment API CI systems can create or update deployment markers without a browser. Use a site API key whose permissions include `annotations:write`, `write` or `*`. ```bash curl -X POST https://purestats.io/api/v1/annotations/deployments \ -H "Authorization: Bearer $PURESTATS_API_KEY" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "external_id": "release-2026-09-21.1", "deployed_at": "2026-09-21T11:30:00Z", "title": "Release 2026-09-21.1", "description": "Checkout and navigation update" }' ``` `external_id` is idempotent per site. Sending it again updates the existing marker instead of creating a duplicate. If omitted, PureStats generates a unique ID. Successful responses use HTTP 201 for a new deployment and HTTP 200 for an update: ```json { "success": true, "data": { "id": 42, "status": "created" } } ``` API keys are restricted to their own site. Keep them in the secret store of the deployment system and never include them in source code, URLs or build logs. --- ## Dashboard filters Source: https://purestats.io/docs/features/dashboard-filters.md Filters narrow the dashboard to a specific segment of visitors or pageviews. They are applied through the URL, so filtered views can be shared with teammates who have access to the same site. ## Available filters PureStats supports filters for: - Pathname and landing page. - Referrer and traffic source. - UTM source, medium, campaign, term and content. - Country, device, browser and operating system. - Goal and event names. - Bot, spam and AI traffic modes. ## Applying filters Open the filter panel, choose a dimension, select an operator and enter a value. After saving, the dashboard reloads with the filter encoded in the URL. Example encoded filter: ```json { "utm_campaign": "spring_launch", "source": "newsletter" } ``` ## Removing filters Active filters appear as removable chips. Traffic mode filters such as bot, spam and AI traffic can also be controlled through the separate traffic selector when enabled for the dashboard. ## Saved segments If you reuse a filter combination, save it as a segment. Segments can be personal, site-level or used as dashboard defaults depending on your permissions. ## Performance notes Very broad custom combinations may take longer on high-traffic sites, especially across large date ranges. --- ## Saved segments and custom dashboards Source: https://purestats.io/docs/features/saved-segments-custom-dashboards.md Saved segments turn repeated filters into named views. Custom dashboards let each user choose which widgets matter most. ## Saved segments A segment stores: - Site. - Filters. - Period preference. - Traffic mode. - Owner. - Visibility. Use segments for recurring questions such as "organic traffic from Germany", "newsletter campaign visitors" or "pricing page visitors on mobile". ## Favorite segments Favorite segments appear first in the dashboard. This is useful when a team has many saved views but each user works with only a few daily. ## Shareable URLs PureStats keeps filters in the URL. A teammate with access to the site can open the same filtered dashboard from a copied link. ## Custom dashboards Custom dashboards define widget order and visibility. A marketing dashboard might prioritize campaigns, goals and Search Console. A product dashboard might prioritize funnels, user journeys and custom events. ## Default views Users can choose a custom dashboard or segment as their start view. Site owners can define recommended team defaults where available. ## Maintenance Review saved segments periodically. Remove unused segments, rename vague filters and avoid duplicate definitions that answer the same question. ## Dashboard templates and shortcuts Choose **Start from template** for Content, Marketing or Shop. Review the selected widgets and save a new dashboard; templates do not overwrite an existing dashboard. Shop prioritizes goal conversions and revenue where revenue goals are configured. In **More**, pin up to five report shortcuts. Preferences belong to your account and apply across websites. Navigation retains the current period, comparison and supported filters. Compact metric cards show two columns on mobile. --- ## Campaign and UTM analytics Source: https://purestats.io/docs/features/campaign-utm-analytics.md Campaign analytics groups visits by UTM parameters and attributes conversions back to the landing session. ## Supported parameters PureStats reads standard UTM fields: | Parameter | Example | Use | | --- | --- | --- | | `utm_source` | `newsletter` | Where the traffic came from. | | `utm_medium` | `email` | Marketing channel. | | `utm_campaign` | `spring_launch` | Campaign name. | | `utm_term` | `running+shoes` | Paid search term or audience. | | `utm_content` | `hero_cta` | Creative or link variant. | ## Landing pages The campaign view shows which pages visitors landed on. Use this to compare whether one campaign performs better on a dedicated landing page or a normal product page. ## Conversion attribution When a visitor converts, PureStats can attribute the conversion to the campaign parameters from the session. This lets you answer questions such as: - Which campaign generated signups? - Which medium has the best conversion rate? - Which landing page drove goal completions? ## Naming rules Use consistent lowercase naming: ```text utm_source=newsletter utm_medium=email utm_campaign=launch_2026_q2 ``` Avoid changing names mid-campaign because analytics will treat each spelling as a separate value. ## Filtering campaign traffic Click a campaign row or use dashboard filters to inspect pages, devices, countries and goals for the same campaign segment. --- ## Custom events Source: https://purestats.io/docs/features/custom-events.md Custom events record actions that are not pageviews. They are useful for signups, purchases, form completions, button clicks and product interactions. ## Track an event ```js window.purestats?.track('Signup Completed'); ``` Event names should be human-readable and stable. Avoid dynamic names like `Plan Clicked: Pro`; use properties instead. ## Add properties ```js window.purestats?.track('Plan Selected', { plan: 'pro', billing_cycle: 'annual' }); ``` Properties let you filter and group events without creating dozens of event names. ## Server-side events Use the Events API when the browser cannot reliably send the event, for example after a payment webhook or backend-only conversion. See [Events API](/docs/reference/events-api). ## Events and goals Any custom event can become a goal. After creating the goal, PureStats shows conversion count, conversion rate, conversion time and attribution by source, campaign and landing page. ## Naming recommendations - Use title case for event names: `Trial Started`. - Use lowercase snake case for properties: `plan_id`. - Keep names stable across releases. - Do not include personal data in event names or properties. --- ## No-code events Source: https://purestats.io/docs/features/no-code-events.md No-code events let marketers or site owners track common interactions without writing JavaScript event handlers. ## CSS class tracking Add a PureStats event class to a clickable element: ```html Start free trial ``` When a visitor clicks the element, the tracker sends a custom event named `Signup Clicked`. ## Data attributes Use data attributes when you want explicit names and properties: ```html ``` PureStats sends the event with properties: ```json { "plan": "pro", "location": "pricing" } ``` ## Forms Track a form submission by adding the same attributes to the `
` element. The tracker records the submit action, not the full form contents. ## Privacy rules Do not put email addresses, names, phone numbers or free-text form values into event properties. Use stable categories or IDs that are safe for analytics. ## When to use JavaScript instead Use the JavaScript API when the event depends on application state, backend validation or complex product logic. --- ## Goals and funnels Source: https://purestats.io/docs/features/goals-and-funnels.md Goals define the actions that matter. Funnels connect several actions or page visits into a sequence and show where visitors drop off. ## Goal types PureStats supports common goal types: - Pageview goals, such as visiting `/thank-you`. - Custom event goals, such as `Signup Completed`. - No-code event goals, such as a clicked CTA. - Campaign-linked goals through UTM attribution. ## Create a goal Open Site Settings, choose Goals, and define the matching rule. Use a clear name that matches the business outcome, not the implementation detail. Example: ```text Goal name: Trial Started Event name: Signup Completed ``` ## Build a funnel A funnel is an ordered sequence: 1. Landing page viewed. 2. Pricing page viewed. 3. Signup started. 4. Trial started. PureStats calculates step conversion, drop-off and time between steps. ## Segment funnels Apply dashboard filters to inspect funnel behavior by source, campaign, country, device or landing page. This helps identify whether a campaign sends traffic that starts the funnel but fails later. ## Keep funnels maintainable Use stable page paths and event names. If your app changes URLs often, prefer custom events for key funnel steps. --- ## Advanced funnels and user journeys Source: https://purestats.io/docs/features/advanced-funnels-journeys.md Advanced funnel and journey reports help explain what visitors do before and after conversion steps. ## Path exploration Path exploration starts from a selected page or event and shows common next steps. Use it to understand whether visitors move from a blog post to pricing, return to the homepage or leave the site. ## Drop-off analysis Funnels show drop-off at each step. Segment the funnel by: - Campaign. - Source. - Device. - Country. - Landing page. - New versus returning sessions. This makes weak steps easier to diagnose. A mobile-specific drop-off usually needs a different fix than a poor campaign match. ## Conversion time Conversion time measures how long visitors take to reach a goal from the first relevant touch in the session. Use it to decide whether a conversion is immediate, research-heavy or dependent on a later return. ## Backtracking paths Backtracking shows where visitors go after failing a step. For example, visitors who leave checkout might return to pricing or support pages. Those paths can reveal missing information. ## Campaign attribution PureStats keeps UTM and source context with funnel sessions so you can compare conversion paths by campaign, not only by final conversion count. --- ## Search performance Source: https://purestats.io/docs/features/search-console.md The search performance integration brings organic query data from Google Search Console and Bing Webmaster into PureStats. You can connect either provider or both at the same time, compare their results and view combined totals. ## What the integration adds After connection, PureStats can show: - Search queries, including terms with impressions but no clicks. - Pages receiving organic search clicks. - Impressions and clicks. - Click-through rate. - Average position. - Search data next to pageviews and conversions. ## Requirements You need: - A verified Google Search Console or Bing Webmaster property. - Permission to read that property's search performance data. - A PureStats site whose domain matches the provider property. - For Google: the PureStats Google OAuth integration configured by the server administrator. - For Bing: Bing OAuth configured by the server administrator, or a Bing Webmaster API key from your account. ## Connect a property Open Site Settings, go to Integrations, and use the Search Console section. Connect Google through OAuth. Connect Bing through OAuth or enter a Bing Webmaster API key as a fallback. Credentials are encrypted at rest. After the provider redirects back to PureStats, select the matching property. Google and Bing connections are independent. Disconnecting one provider removes only that provider's imported rows and leaves the other connection unchanged. ## Compare providers The Search performance card in Analytics includes a provider selector. Choose All providers for combined clicks and impressions, or select Google or Bing to inspect a provider separately. Each imported row retains its provider so overlapping Google and Bing values remain traceable. ## Data freshness Search performance data is not realtime. Providers publish it with a delay, and Bing query statistics may update less frequently than Google. PureStats imports available rows during provider-specific scheduled sync jobs. ## SEO opportunities Open **Analytics → More → SEO Opportunities** after connecting a matching property and completing a sync. Choose a date range and provider. The last three UTC days are excluded, and a range of at least seven completed days with successful sync coverage is required. The view reviews up to 500 page URLs per provider, ordered by impressions, and lists up to 30 opportunities. It flags: - At least 200 impressions, average position 1–10 and click-through rate below 2%: review the title, description and search intent. - At least two positions lost against the preceding period, with at least 200 impressions in both periods: investigate indexing, content changes and competing results. Positions are weighted by impressions. Priority combines the click gap to the 2% review threshold, position deterioration and the conversion performance of tracked human organic visits to the landing page. The click gap is an estimate, not a forecast. Conversion rates require at least 20 visits and no known tracking gaps. Each visit is counted once even if it converts several times; conversions are not attributed to individual search queries. Page, country and device filters apply to the opportunities. Bing does not supply country or device dimensions; clear those filters to include it. Unsupported analytics filters remain in navigation and are explicitly identified as excluded from search calculations. Untracked property hostnames are excluded from the list. Missing or incomplete preceding sync coverage is shown as unavailable and cannot produce a position-decline flag. A successful range refresh replaces stale rows; transient errors are retried by the scheduled sync, while expired authorization requires reconnection. Changing properties clears the old property's rows and comparison coverage. ## Troubleshooting If no properties appear, verify that the connected account has access to a verified property. If a property appears but no data imports, confirm the site domain, provider status and selected property. A reconnect warning applies only to the affected provider. ## Query detail and brand filters Each opportunity shows current and previous clicks, impressions, CTR and weighted position, plus up to five queries ranked by current impressions. Choose All, Branded or Non-branded queries. Enter up to five comma-separated brand terms; matching is case-insensitive and treats wildcard characters literally. Device and two-letter country filters remain available. Brand filters affect search rows, not onsite conversion rates. Save an opportunity as an [action](/docs/features/actions) to assign it and record a change date. ## Google 403: access_denied during testing If Google says the app is testing and only approved testers can sign in, the Google Cloud project owner must open **Google Auth Platform → Audience → Test users → Add users**, add the exact Google-account email and save. Then retry connecting from PureStats. Ensure the project owns the OAuth client configured for PureStats. See [Google's audience instructions](https://support.google.com/cloud/answer/15549945). For access by all users, the project owner must publish the app and complete Google's required sensitive-scope verification. In external Testing mode, authorization using Search Console scopes expires after seven days and must be renewed. See [OAuth token expiry](https://developers.google.com/identity/protocols/oauth2) and [sensitive-scope verification](https://developers.google.com/identity/protocols/oauth2/production-readiness/sensitive-scope-verification). Changing a redirect URL or reconnecting alone does not authorize an unlisted tester. --- ## Consolidated view Source: https://purestats.io/docs/features/consolidated-view.md Consolidated view combines multiple PureStats sites into one dashboard. It is useful for agencies, product suites, content networks and teams that manage several domains. ## What is combined The view can aggregate: - Visitors. - Pageviews. - Current visitors. - Sources. - Campaigns. - Top pages. - Locations. - Devices. - Goals and events. ## Site selection Choose all accessible sites or a subset. PureStats only includes sites your account can access. ## Filtering Most dashboard filters also work in consolidated view. Site becomes an additional dimension, so you can compare one domain against the rest. ## Use cases - Agency overview across client sites. - SaaS product plus marketing site plus docs. - Multiple regional domains. - Content network performance. ## Limitations Some reports require compatible configuration across sites. For example, a goal named `Signup Completed` can be combined cleanly only when the same event name means the same thing on each site. --- ## Exports Source: https://purestats.io/docs/features/exports.md PureStats supports CSV exports for reporting, auditing and external analysis. ## Small exports For small time ranges, exports can complete immediately. Choose the site, period, filters and export type, then download the CSV. ## Large export jobs Large sites should use background export jobs. PureStats queues the job, processes it outside the web request and provides a download link when it is ready. Export jobs include: - Status. - Requested site and period. - Filters. - Row count where available. - Created and finished timestamps. - Download expiration. ## Email notification If enabled, PureStats sends an email when the export is complete. Link clicks and opens are tracked in the mail system for delivery diagnostics. ## Security Only users with access to the site can create or download exports. Download links should be treated as sensitive because CSV files can contain detailed operational data. ## Tips - Use filters to reduce export size. - Prefer scheduled reports for recurring summaries. - Use scheduled reports when you need the same metrics weekly or monthly. --- ## Scheduled reports Source: https://purestats.io/docs/features/scheduled-reports.md Scheduled reports deliver analytics summaries without requiring users to open the dashboard. ## Report contents Reports can include: - Visitors and pageviews. - Top pages. - Sources and campaigns. - Goals and conversions. - Traffic changes. - Automatic weekly insights with landing-page and source changes, filtered report links and data-quality notes. ## Schedule options Choose weekly or monthly delivery. Reports use the site's timezone, so a Monday weekly report reflects the local reporting period for that site. The schedule timezone defaults to the site's timezone and can be changed. Select **Automatic weekly insights** in the report contents, or start with the Weekly pulse template. Existing schedules keep their selected contents; enable this section to receive the new insights. Weekly insights always describe the last completed Monday–Sunday week in the site's timezone, including in monthly reports. They use native human session starts, deduplicate stitched sessions and exclude historical imports. Their links open the exact dates and dimension filters. Known data gaps, a new site or fewer than 20 visits in either week prevent explanatory comparisons. Traffic changes describe observed counts and do not establish their cause. ## Recipients Recipients must have access to the site or be explicitly allowed by a site owner. Keep recipient lists short and relevant. ## Email preferences Users can manage report preferences in their profile. Site owners can manage schedules from Site Settings. ## Delivery troubleshooting If a report does not arrive, check your email preferences, recipient list and spam folder. If delivery still fails, contact PureStats support. --- ## Alerts and monitoring Source: https://purestats.io/docs/features/alerts-monitoring.md Alerts help you detect analytics problems before they become silent data gaps. ## Alert types PureStats can monitor: - Tracking stopped. - Traffic unusually increased or decreased. - Goal conversions dropped. - Scheduled report or alert delivery appears delayed. ## Alert rules Each alert rule has a site, condition, threshold, notification target and cooldown period. Cooldowns prevent repeated notifications for the same ongoing issue. ## Alert events When a rule triggers, PureStats creates an alert event. Events show when the condition started, when it recovered and whether a notification was sent. ## Notifications Alert notifications are sent by email to the configured recipients. In **Profile → Email preferences**, enable or disable each alert type for your account's email address across all websites. Re-enabling removes your own unsubscribe suppression; delivery blocks caused by bounces or administrator decisions still apply. Reports and account emails have separate settings. Choose immediate delivery or a daily summary for each type. Summaries group the previous day's events by email address and alert type and are queued from **08:00 Europe/Berlin**. Each immediate mail and summary includes a link to unsubscribe from exactly its type; opening the link only shows a confirmation page. Durations are displayed as `45m`, `2h 15m` or `3d 4h`. ## Weekday comparison Traffic spike and drop rules can compare with the same local time in the preceding four weeks. Select **Same weekday · four weeks** in the rule. This mode requires four weeks of tracking history, an evaluation window of at most 24 hours, sufficient baseline traffic and no known tracking gaps in the compared windows. Without those prerequisites it does not trigger a change alert. Existing rules retain their previous-window comparison until you change them. ## Recommended rules For production sites, start with: - No tracking hits for 24 hours. - Traffic down more than 80% compared with the same period. - Rollup job delayed. Adjust thresholds after you understand normal traffic variance. --- ## Site settings Source: https://purestats.io/docs/sites/site-settings.md Site Settings control how PureStats accepts, processes and reports data for one site. ## Main sections Typical settings include: - General site details. - Domains and aliases. - Tracking verification. - Goals and funnels. - Team access. - Reports. - Alerts. - Privacy Center. - Integrations. ## General settings Review the canonical domain, timezone and display name. The timezone affects dashboard periods and reports, so set it before relying on weekly or monthly comparisons. ## Tracking settings Use tracking settings to manage path exclusions, bot filtering, cross-domain stitching, consent mode and tracker verification. ## Goals and funnels Define conversion goals and funnel steps. Keep names stable so historical reports remain meaningful after product releases. ## Team access Invite teammates and assign roles. Give users the least access they need. Site owners can manage settings; viewers should only inspect dashboards and reports. ## Reports and alerts Scheduled reports are for routine summaries. Alerts are for unexpected changes such as tracking outages, traffic anomalies or goal drops. ## Recommended review cycle Review Site Settings after adding domains, changing your consent banner, launching a campaign or inviting new team members. ## Intentionally inactive websites Turn off **Monitor this active website** when a site is deliberately inactive. This pauses its alert rules and labels monitoring as paused, while incoming data is still accepted. Recent real tracking evidence determines live health: more than 24 hours without a request is stale, and more than seven days is inactive. Diagnostic test pixels alone do not confirm real tracking. --- ## Site aliases Source: https://purestats.io/docs/sites/site-aliases.md Site aliases let one analytics site accept events from multiple allowed hostnames. ## Example Canonical site: ```text example.com ``` Allowed aliases: ```text www.example.com app.example.com checkout.example.com ``` PureStats stores aliases separately and resolves incoming events through the canonical site ID. The matching `www.` or non-`www.` hostname is added automatically when available. ## Why aliases matter Without aliases, a tracker running on a different hostname such as `app.example.com` would be rejected if the configured site is `example.com`. Aliases prevent accidental data loss while still blocking invalid or malicious hostnames. ## When to use aliases Use aliases for: - App and marketing subdomains. - Checkout domains. - Documentation or help center domains owned by the same product. Do not use aliases for unrelated client sites. Create separate sites instead. ## Cross-domain sessions Aliases allow events to be accepted. Cross-domain session stitching is a separate setting that helps connect a visitor journey across those allowed hostnames. ## Verification After adding an alias, open a page on that hostname and check Site Health. The latest hit should show the alias hostname and accepted status. --- ## Path exclusions Source: https://purestats.io/docs/sites/path-exclusions.md Path exclusions prevent PureStats from recording pageviews or events for matching paths even when the tracker is installed there. Configure exclusions in Site Settings. The tracker loads these rules automatically, so the default script tag still only needs `data-domain`. ## Common exclusions Examples: ```text /admin /admin/* /account/* /preview/* /internal/* ``` Use exclusions when the tracker is included through a shared layout but some areas should not appear in analytics. ## Matching behavior Rules should be simple and predictable: - Exact paths match only that path. - Prefix or wildcard-style rules match child paths. - Query strings are ignored for path matching. Keep rules narrow. Excluding `/app/*` may remove important product analytics if your app lives there. ## Private and staff traffic Path exclusions are different from internal traffic filtering. Exclusions remove specific URLs for everyone. Internal traffic filters remove visits from known staff or environments. ## Debugging exclusions If a page does not show up: 1. Check Site Settings for exclusion rules. 2. Confirm the browser path matches the rule. 3. Switch dashboard traffic mode to all traffic. 4. Inspect the Network response from `/api/event`. ## Privacy benefit Excluding private areas reduces the chance that account URLs, billing paths or internal workflows appear in analytics reports. --- ## Privacy Center Source: https://purestats.io/docs/privacy/privacy-center.md The Privacy Center brings site-level privacy settings into one place. ## Controls shown The Privacy Center summarizes: - IP anonymization. - Data retention. - Bot and spam filtering. - Cross-domain session stitching. - Consent mode. - Export and delete request workflow. - Tracking status. ## IP anonymization IP anonymization reduces identifiability before long-term analytics processing. Use it by default unless you have a clear operational reason not to. ## Retention Retention controls how long raw analytics data is kept. Rollups can preserve aggregate trends while old raw pageviews, events and sessions are removed. ## Bot filtering Bot and spam filtering keeps automated traffic out of normal reports. Site owners can inspect all traffic separately when debugging. ## Consent mode Consent mode lets the tracker wait for a consent signal before sending events. This is useful for sites that need explicit analytics consent in certain jurisdictions. ## Export and delete requests If a visitor or customer requests data export or deletion, the Privacy Center documents the operational process and links to the relevant support workflow. ## Auditability Privacy settings should be reviewed after major product changes, new domains, new integrations or changes to legal requirements. --- ## Bot and spam filtering Source: https://purestats.io/docs/privacy/bot-spam-filtering.md PureStats filters invalid domains before storing events. Bot and spam filtering handles the next problem: automated traffic on valid domains. ## Signals Filtering can use: - Known bot user-agent signatures. - Suspicious user-agent patterns. - Empty or malformed headers. - High request frequency. - Repeated event patterns. - Known AI crawler identifiers. - IP and network heuristics where available. ## Site setting Each site can choose whether normal dashboard views exclude bot traffic. Site owners can switch traffic modes when they need to inspect all traffic for diagnostics. ## Traffic modes The dashboard traffic selector can show: - Clean traffic. - All traffic. - Bot traffic. - Spam traffic. - AI traffic. Use all traffic when verifying the tracker or investigating why a visit was excluded. ## False positives No heuristic is perfect. If legitimate traffic is filtered, inspect the event metadata, user agent, path and rate. Adjust filtering rules conservatively so you do not pollute historical analytics. ## Best practices - Keep bot signatures updated. - Avoid tracking staging domains as production traffic. - Use path exclusions for private pages. - Use rate-limit scoring instead of a single hard rule when possible. --- ## IP anonymization and retention Source: https://purestats.io/docs/privacy/ip-anonymization-retention.md PureStats is designed to keep useful analytics while reducing unnecessary raw-data exposure. ## IP anonymization When IP anonymization is enabled, PureStats reduces IP precision before long-term processing. This helps preserve location and abuse-protection utility without retaining the full address indefinitely. ## Raw data Raw pageviews, events and sessions provide detailed reports and exports. They can grow quickly on busy sites, so retention matters for both privacy and performance. ## Rollups Aggregate reports keep metrics such as daily visitors, pageviews, sources, campaigns and goals. They make dashboards faster and allow old raw data to expire while preserving trend reporting. ## Retention jobs Retention jobs should: - Keep configured raw-data windows. - Preserve aggregate reports. - Remove expired export files. - Report retention problems in user-facing health notices where available. ## Choosing a retention period Choose the shortest period that supports your reporting needs. A product team may need longer journey data than a small content site, but both should avoid indefinite raw-data retention by default. ## What users should check Site owners should confirm that retention settings match their reporting needs and privacy policy. --- ## Consent mode Source: https://purestats.io/docs/privacy/consent-mode.md Consent mode lets sites control when PureStats sends analytics events. ## Default behavior Without consent mode, the tracker sends pageviews when it loads, subject to site settings such as path exclusions, domain validation and bot filtering. ## Consent-gated behavior When consent mode is enabled, the tracker waits until your consent banner or CMP grants analytics permission. Example: ```js window.purestats?.consent.grant(); ``` If consent is denied, call `window.purestats?.consent.deny()`. The consent mode itself can be managed in Site Settings; you do not need to add `data-consent-mode` to the tracker snippet. ## Integrating with a CMP Connect your consent management platform to PureStats by calling the consent method when the visitor chooses analytics consent. Store consent according to your legal and product requirements. ## Revoking consent If a visitor revokes consent during a session, call: ```js window.purestats?.consent.revoke(); ``` After this, your application should stop sending analytics events. ## Verification When consent mode is active, Site Health may show script found and script loads before first hit received. That is expected until consent is granted. --- ## Team members Source: https://purestats.io/docs/account/team-members.md Team access controls who can view dashboards, manage settings and operate reports for a site. ## Roles Use roles to keep access clear: - Viewer: can inspect dashboards and reports. - Analyst: can use filters, segments, exports and reports. - Manager: can manage goals, funnels and site settings. - Owner: can manage team access and sensitive site settings. Exact permissions may vary by deployment, but the principle is the same: grant only the access needed. ## Inviting users Site owners can invite users by email. New email users must confirm their address before they can sign in. ## Removing access Removing a user from a site only removes that user's access to the site. It does not delete their PureStats account. ## Audit events Access changes should be reviewed by site owners regularly, especially after team changes or contractor offboarding. ## SSO users Users who sign in through Google or GitHub can still be added to teams like email users. Connected identities are visible in the user's profile. --- ## SSO, 2FA and sessions Source: https://purestats.io/docs/account/sso-2fa-sessions.md Account security settings live in the profile area. ## Connected accounts PureStats supports Google and GitHub SSO. Users can connect or disconnect providers from their profile when it is safe to do so. SSO-only accounts should set a password if they want email/password login as a fallback. ## Two-factor authentication 2FA adds a time-based one-time password step after login. During setup: 1. Scan the QR code with an authenticator app. 2. Enter a current code. 3. Store recovery codes if provided. 4. Confirm that 2FA is marked active. If setup fails, check device time synchronization and retry with a fresh code. ## Active sessions The profile shows active sessions. Users can end individual sessions or log out all other devices. ## Sensitive changes After password changes or 2FA reset, PureStats can revoke other sessions. This limits risk when account credentials change. --- ## Measurement methodology and verification Source: https://purestats.io/docs/reference/measurement-methodology.md PureStats is free with unlimited sites and tracked traffic. Unlimited product access does not remove payload validation, rate limits or protection against abuse. This guide explains what our numbers mean and how to verify collection without inflating real reports. ## Visitors are not people identified across the internet Default anonymous tracking uses site-scoped, daily rotating visitor hashes. A browser can therefore appear as a new anonymous visitor on another day. Browsers that block or do not run the script may never produce a browser pageview. Server-side requests, AI crawlers and human browser visits are different signals: compare them within the same traffic mode rather than treating every HTTP request as a person. Optional persistent identity changes retention coverage. It must be deliberately configured; raw identity values are not stored. See [retention analytics](/docs/features/retention-analytics) for coverage and cohort definitions. Visits, unique visitors, events and conversions are different metrics, even when they come from the same activity. ## Reproducible tracker-size evidence The [lightweight tracker page](/lightweight-analytics-script) reads the current core version and Gzip size from the generated build manifest. It is not an estimate or an old marketing claim. The source, minified output and checksums are validated together in CI. Optional Web Vitals, experiments, search and replay are separate downloads and are not included in the core measurement. Download the current `https://purestats.io/pf.min.js` and measure its Gzip size using `gzip -n -9 -c pf.min.js | wc -c`. The `-n` switch excludes the local filename and timestamp. Browser transfer size can differ because of compression settings, HTTP headers, caching and protocol overhead. A small script is useful but does not guarantee a particular Lighthouse or Core Web Vitals result. ## Worked verification: the proxy-IP trap A common first-party deployment mistake is to forward the event body but lose the browser's client address. Every visitor then appears to come from the proxy server. Adding an arbitrary `X-Forwarded-For` header from the browser is not a safe correction: visitors can forge that header. 1. Register the proxy's egress addresses or CIDRs in the site's approved proxy settings. 2. Resolve the client address at the trusted server or edge connection. Discard visitor-supplied forwarding and PureStats signature headers. 3. Forward the untouched request body, trusted client context and, where configured, a server-generated HMAC signature. Keep signing material in server-side secret storage. 4. Cache only successful tracker JavaScript. Do not cache event ingestion, replay uploads or authenticated APIs. 5. Run an [isolated tracking test](/docs/features/tracking-test-mode), check accepted and rejected results, and verify the script, configuration and event paths independently. 6. Confirm a subsequent genuine visit in the intended timezone and traffic mode. Do not inject fake conversions just to make a dashboard appear active. The [first-party proxy guide](/docs/installation/first-party-proxy) contains the actual paths, Nginx, Apache and signed Worker examples. Proxying cannot promise complete collection or bypass a visitor's consent choice. The same privacy settings remain in force. ## Comparing tools and imported history Align timezone, date boundaries, bot inclusion, consent rules and reporting identity before comparing totals. A server log records requests, a browser tracker records successful script execution, and an imported aggregate report records the provider's own reporting model. Differences are not automatically lost data. [Historical imports](/docs/features/historical-imports) remain labeled aggregates; they do not become artificial sessions or visitor profiles. Overlapping import and native periods are not silently summed. The [Google Analytics comparison](/google-analytics-alternative) and [Plausible comparison](/plausible-alternative) link to provider documentation and state their review dates. Features shared by products are not presented as exclusive to PureStats. ## Discovery is not the same as adoption Search-engine impressions, a crawler fetching documentation, a human referral from an AI assistant and a successfully configured agent site are separate outcomes. `llms.txt` is a documentation entry point, not authentication, a ranking guarantee or proof that an assistant cited a page. [Agent documentation](/docs/agents/index) states which APIs are live and which plugin integrations are still unavailable. ## Publisher and corrections PureStats publishes these definitions and checks product claims against the implemented behavior. For a suspected measurement error, contact [support@purestats.io](mailto:support@purestats.io) with the domain, timeframe, timezone and traffic mode. Do not include tokens, recovery material or unredacted visitor details. See [About PureStats](/about) and the [privacy controls](/docs/privacy/privacy-center). --- ## Tracking script reference Source: https://purestats.io/docs/reference/tracking-script.md The canonical PureStats tracker is `pf.js`. The default installation only needs `data-domain`; site-level behavior is loaded from PureStats automatically. ## Basic snippet ```html ``` ## Attributes | Attribute | Required | Description | | --- | --- | --- | | `data-domain` | Yes | Canonical PureStats site domain. | | `data-api` | No | Custom event endpoint. Defaults to PureStats. | | `data-config-api` | No | Custom tracker configuration endpoint. Derived from `data-api` by default. | | `data-consent-mode` | No | Explicit client-side consent mode override. Prefer Site Settings. | | `data-respect-dnt` | No | Explicit Do Not Track override. Prefer Site Settings. | | `data-exclude-paths` | No | Client-side path exclusions for advanced deployments. Prefer Site Settings. | | `data-cross-domain` | No | Explicit cross-domain stitching override. Prefer Site Settings. | | `data-allowed-domains` | No | Explicit comma-separated alias list for cross-domain stitching. Prefer Site Settings. | | `data-linker` | No | Enables or disables the optional cross-domain linker parameter. | ## First-party proxy For first-party proxy setups, load `pf.min.js` from your own domain and set `data-api` to your proxied event endpoint: ```html ``` The tracker derives `https://example.com/api/tracker-config` from `data-api` unless `data-config-api` is set explicitly. See [First-party proxy](/docs/installation/first-party-proxy) for server configuration examples. When Core Web Vitals, active experiments, no-code events, site search or session replay require optional functionality, the core tracker loads `/pf-vitals.min.js`, `/pf-experiments.min.js`, `/pf-events.min.js`, `/pf-search.min.js` or `/pf-replay.min.js`. First-party proxies must forward and may cache these files in the same way as `/pf.min.js`. ## Event endpoint The tracker sends events to: ```text https://purestats.io/api/event ``` The API validates the domain or alias, path exclusions, consent state and bot/spam scoring before storing the event. Before the first event, the tracker also reads: ```text https://purestats.io/api/tracker-config ``` That response applies the current Site Settings without requiring extra attributes in the script tag. ## Browser support The tracker is designed for modern browsers. If a browser blocks JavaScript, analytics events cannot be collected. ## Versioning PureStats builds the minified tracker from source and exposes version/checksum metadata for release checks. Use `pf.js` as the public integration file. --- ## JavaScript API Source: https://purestats.io/docs/reference/javascript-api.md The tracker exposes a small JavaScript API on `window.purestats`. ## Track a pageview ```js window.purestats?.('pageview'); ``` ## Track an event ```js window.purestats?.('Signup Completed'); ``` With properties: ```js window.purestats?.('Signup Completed', { source: 'header' }); ``` ## Identify a signed-in user Use a stable, opaque internal ID to connect visits from the same signed-in user. PureStats transforms the value with a site-specific HMAC before storage. ```js purestats('identify', 'internal-user-42') // or, after the tracker has loaded purestats.identify('internal-user-42') ``` Visitor-scoped custom properties can be supplied with the identity call after defining them in **Site settings → Custom dimensions**: ```js purestats.identify('internal-user-42', { account_type: 'customer', trial_active: true }) ``` Call `purestats('resetIdentity')` or `purestats.resetIdentity()` when the user logs out or switches accounts. Never send names, email addresses, phone numbers, or other directly identifying values. ## Set consent ```js window.purestats?.consent.grant(); ``` Disable tracking after consent is revoked: ```js window.purestats?.consent.revoke(); ``` ## Assign and expose an experiment Create and start the experiment in **Analytics → More → Experiments**. PureStats then loads the optional experiment module from the server-side tracker configuration. Wait for the ready event before assigning a variant: ```js window.addEventListener('purestats:experiments-ready', () => { const variant = purestats.assignExperiment('homepage_headline'); document.documentElement.dataset.headlineVariant = variant; purestats.trackExposure('homepage_headline', variant); }); ``` `assignExperiment()` is deterministic for the browser and configured weights. It does not count an exposure until `trackExposure()` is called, so visitors who never see the tested UI do not enter the result. PureStats stores only a site-scoped hash and never stores the local assignment identifier. ## Load timing Because `pf.js` is usually loaded with `defer`, the API may not exist immediately in inline scripts placed before it. Run custom tracking after the page is ready or after your application initializes. ## Data safety Do not send personal data in event names, paths or properties. --- ## Events API Source: https://purestats.io/docs/reference/events-api.md Use the Events API for backend-side events, webhook conversions or systems where browser tracking is not reliable. ## Endpoint ```http POST https://purestats.io/api/event Content-Type: application/json ``` ## Example payload ```json { "domain": "example.com", "name": "Payment Completed", "url": "https://example.com/checkout/success", "path": "/checkout/success", "referrer": "https://example.com/pricing", "props": { "plan": "pro", "billing_cycle": "annual" } } ``` ## Validation The API validates: - Domain or alias. - URL and path format. - Bot or spam signals. - Payload size. - Required fields. Invalid payloads are rejected and should not enter analytics tables. ## Authentication Browser tracker calls rely on domain validation. Server-side integrations should use configured API credentials when available for your deployment. ## Retry behavior For important backend events, retry temporary failures with backoff. Do not retry validation errors without changing the payload. --- ## Measurement Protocol and server SDKs Source: https://purestats.io/docs/reference/measurement-protocol.md # Measurement Protocol and server SDKs Use the Measurement Protocol when an event happens outside a browser. Open **Site settings → Measurement Protocol**, create an ingest key and store it in your server-side secret manager. Keys are shown once, can expire and can be rotated without downtime. Never embed an ingest key in browser or mobile application code. Calls from public clients should go through your backend. ## Endpoint Send one event or a batch of up to 100 events to `POST https://purestats.io/api/v1/events`: ```http Authorization: Bearer ps_ingest_... Content-Type: application/json ``` ```json { "events": [ { "type": "event", "event_id": "0123456789abcdef0123456789abcdef", "name": "Signup", "user_id": "your-internal-pseudonymous-id", "properties": { "method": "email" }, "context": { "page": { "url": "https://example.com/register" } } } ] } ``` Supported types are `page`, `screen`, `event`, `identify`, `experiment` and `commerce`. Event IDs are deduplication keys and must contain 12–32 URL-safe characters. Missing IDs are generated by PureStats, but SDKs generate them before the first attempt so retries remain idempotent. Each result is `stored`, `duplicate`, `filtered`, `invalid` or `error`. A mixed batch returns HTTP 207 and keeps the outcome of every event independent. ## Visitor IP forwarding The sender's server IP is never interpreted as a visitor IP. The optional `X-PureStats-Client-IP` header is accepted only with a timestamp no older than five minutes and an HMAC-SHA256 signature made with the ingest key: ```text signature = HMAC_SHA256( + "\n" + + "\n" + SHA256(), ingest_key) ``` Send the timestamp in `X-PureStats-Client-IP-Timestamp` and the lowercase digest in `X-PureStats-Client-IP-Signature`. Site IP-anonymization and geo-precision settings still apply. ## Official SDK source Publish-ready Node/TypeScript, PHP, Kotlin and Swift packages live in the PureStats SDK source tree. They provide stable event IDs, batches, retry with backoff and an in-memory offline queue. Persist the queue in your application's durable job system when delivery must survive a process restart. --- ## Export API Source: https://purestats.io/docs/reference/export-api.md The Export API is used to request analytics exports programmatically where enabled. ## Small exports Small exports may return a downloadable response immediately. Include the site, period and filters. ## Large exports Large exports should create a background job: ```json { "site_id": 123, "period": "last_month", "format": "csv", "filters": { "utm_campaign": "spring_launch" } } ``` The response contains a job ID. Poll the job status until it is complete, then download the result. ## Job states Common states: - `queued` - `processing` - `completed` - `failed` - `expired` ## Access control The API only returns exports for sites the authenticated user can access. ## Practical limits Use filters and date ranges to keep exports manageable. For recurring summaries, scheduled reports are usually better than repeated large CSV exports. --- ## UTM reference Source: https://purestats.io/docs/reference/utm-reference.md Consistent UTM naming makes campaign analytics easier to read. ## Standard fields | Field | Recommended value style | Example | | --- | --- | --- | | `utm_source` | Platform or source | `newsletter`, `google`, `linkedin` | | `utm_medium` | Channel | `email`, `cpc`, `social` | | `utm_campaign` | Campaign name | `launch_2026_q2` | | `utm_term` | Keyword or audience | `brand_terms` | | `utm_content` | Creative variant | `hero_cta` | ## Naming rules Use lowercase, stable separators and no spaces: ```text utm_source=newsletter utm_medium=email utm_campaign=pricing_update_2026 utm_content=footer_link ``` ## Avoid - Mixing `Email`, `email` and `e-mail`. - Using full sentences. - Putting personal data into campaign parameters. - Changing campaign names after launch. ## Attribution PureStats stores campaign values with the landing session and can attribute goals and funnel progress to those values. ## Cleanup If a campaign has many spelling variants, use dashboard filters or exports to identify the variants and standardize future links. --- ## Troubleshooting Source: https://purestats.io/docs/reference/troubleshooting.md Use this checklist when PureStats does not show the data you expect. ## No pageviews appear Check: - The script is present on the page. - `pf.js` loads without a browser error. - `/api/event` returns success. - `data-domain` matches the canonical site. - The current hostname is added as an alias. - The path is not excluded. - Consent mode is not waiting for consent. ## First-party proxy issues If you load PureStats through your own domain, check: - `/pf.min.js` returns JavaScript from your domain. - `/api/event` accepts POST requests and forwards the request body. - `/api/tracker-config?domain=example.com` returns tracker settings. - The proxied script uses `data-api="https://example.com/api/event"`. - Your app fallback route does not catch the proxy paths before the proxy does. - `pf.min.js` may be cached, but `/api/event` should not be cached. ## Current visitors is zero Open the realtime view and switch traffic mode to all traffic. If pageviews exist but current visitors is zero, inspect session writes, session timestamps and bot/spam classification. ## Site appears in another user's dropdown Users should only see sites they own or can access through team membership. If unrelated sites appear, inspect site access queries and role joins. ## Map says library not loaded Confirm the map asset is included on the page and not blocked by CSP or ad blockers. Check the browser console for missing script or CSS files. ## Emails fail Check: - Your email address is correct. - Your account email is verified. - The message is not in spam or promotions. - Your report recipient list includes the intended address. - Your account has not disabled report emails. If delivery still fails, contact PureStats support with the report name and expected delivery time. ## Slow dashboards Try a shorter period, remove very broad filters or use an export job for large date ranges. If dashboards stay slow, contact PureStats support with the affected site, period and filters. ## Still stuck Collect the site domain, request ID, timestamp, dashboard URL and the browser Network response from `/api/event`. This gives support enough context to trace the issue.