Staats

Tracking

A ~1.5KB compressed tracker for automated pageviews and declarative custom events.

Pageviews

The script tag records a pageview on initial load and listens to client-side single-page app routing. It hooks into pushState, replaceState, and popstate, so framework navigation (Next.js, Vite, React, Vue, Svelte) is measured automatically without extra configuration.

<script defer data-site="YOUR_SITE_CODE" src="https://www.staats.ai/s.js"></script>

Custom events

Add a data-staats attribute to any clickable HTML element. No site-side JavaScript needed: the tracker uses a single delegated event listener to capture interactions dynamically:

<button data-staats="signup_click">Start free trial</button>
<a href="/download" data-staats="download_click">Download App</a>

For non-click interactions or programmatic flows, call window.staats:

window.staats('checkout_completed')

When your coding agent instruments an event, have it record what the event means in site context (update_site_context with the events map) so any future agent session can look up event definitions instantly without re-parsing your source code.

Error tracking

The tracker also listens for uncaught JavaScript errors and unhandled promise rejections. Capture is strictly passive: the tracker never intercepts, suppresses, or rethrows anything, so console output, stack traces, and any other error tooling on the page behave exactly as if the tracker were absent.

Error messages are sanitized on our servers before anything is stored: emails, tokens, keys, card-shaped numbers, UUIDs, IPs, and long quoted strings are replaced with placeholders, the message is truncated to 200 characters, and one sample is kept per distinct error, not per occurrence. Errors are grouped by a fingerprint of the sanitized message and source file. Line numbers and bundler content-hashes are deliberately excluded from the fingerprint (they change on every build), so the same error stays one group across deploys; the sample shown always carries the most recent source and line. Ask your agent what_broke to read them, correlated with your deploy annotations.

Error events never touch your monthly event quota. They meter against a separate monthly error allowance set by your plan (every plan has one, free included; see pricing), with the same 25% grace window events get. On top of that they are bounded by 5 per page load in the browser (the budget resets on each navigation) and per-visitor and per-site rate limits on the server, so even a site-wide error storm can never touch the events your plan pays for. Error events are analytics-internal: they are excluded from the CSV traffic export, and errors thrown by cross-origin scripts without crossorigin attribution (which browsers report only as an empty "Script error." stub) are ignored entirely, and so are errors thrown by browser extensions (chrome-extension:// and friends): those are a visitor's extension misbehaving, not your site, so they never reach your error list.

Event naming conventions

Use clean, lowercase snake_case names that follow a target_action pattern: pricing_click, trial_started, export_csv_click. Event names are capped at 120 characters. Consistent naming ensures your agent can query and build funnels over them reliably.

What each event records

FieldDescription
type & namepageview, custom with the event name, or error with the error's fingerprint
pathPage pathname (clean URLs without query strings)
referrer domainExternal referral hostnames only (internal hops filtered)
utm parametersCampaign attribution (utm_source, utm_medium, utm_campaign)
ad click detectionAn ad network's click parameter (gclid, wbraid/gbraid, msclkid, ttclid, twclid, li_fat_id, fbclid) fills in the source when utm_source is absent: google, microsoft, tiktok, twitter and linkedin also claim a paid medium unless utm_medium is set; facebook claims none, since Meta apps append it to organic shares too. The click token itself never leaves the page.
device & browserDerived from User-Agent: desktop / mobile / tablet
operating systemDerived from User-Agent: iOS, Android, ChromeOS, Windows, macOS, or Linux
countryInferred via client timezone (no IP geolocation services; see Data & Privacy)
visitorPseudonymous hash rotating daily (zero cookies, zero persistent storage)

Known search engine crawlers and automated bots are filtered out at ingestion and never count toward your event quota.

Excluding your own visits

Your own browsing counts the same as anyone else's. On a quiet site that can be most of the traffic, and it is enough to trigger a false anomaly. Run this once in the browser console, on the site you want to skip:

localStorage.staats_ignore = 'true'

The flag is stored per browser and per domain, so repeat it in every browser you use and on each site you own. To start counting yourself again, run localStorage.removeItem('staats_ignore').

Local development is already excluded. Pages opened from file:// and any host on localhost are never recorded, so your dev server stays out of production numbers with no setup.

Leave the script off your 404 page

If your 404 page carries the script tag, every mistyped URL and every crawler probe is recorded as a pageview for a page that does not exist, and those paths then sit in your stats looking like real ones. Serve your 404 page without the snippet.