Changelog
What's new in fastmon, covering the dashboard, the API, the platform, and the tracker. Newest first.
The latest from fastmon, across the dashboard, API, platform, and tracker. You can filter the feed by area.
We cover the changes you should know about, not every internal tweak. The exception is the tracker (the script you embed on your site): we document it in full, because it runs in your visitors' browsers, where even a small change can affect what your pages measure.
September 2026
The error rate counts errors again#
The error rate said "share of pageviews with at least one error" and counted every failed image, script and stylesheet into it. The server could only subtract the hosts it recognised from a list of about 50 trackers, and ad-block lists carry tens of thousands, so shops with healthy JavaScript saw error rates around 60 percent. The tracker now counts the two populations apart.
- The error rate is JavaScript errors only: the share of pageviews with at least one uncaught exception or unhandled rejection.
- Failed resource loads are their own signal, with their own rate (
failed_resource_rate), their own curve beside the error rate, their own block at the bottom of the error page, and their own list atPOST /analytics/failed-resources. Each row says whose host it was, yours or a foreign one. The MCP server gainedget_failed_resources. The error-types endpoint now refuseserror_type: ["ResourceLoadError"]with a 400 pointing there, because an empty list would read as "you have none". - The beacon ships two ranked lists. Up to five JavaScript errors followed by up to three failed resources, each ranked inside its own channel, so a page full of broken images cannot push a real exception off the wire. Two counters,
errcandfrc, count every occurrence rather than only the entries that fit. - Your own subdomains count as yours. First party used to mean the exact page host, so a shop serving its bundle from
cdn.shop.examplehad its own errors attributed to a third party and no file path to look at. The rule is now the registrable domain, in one place, for the error rate, the error origin, the Fetch/XHR split and the resource list alike. - The Fetch/XHR error rate follows the same rule. It counts failures on your own hosts and reports failures on foreign hosts as a separate number beside it. A blocked tracker call used to leave the rate only when its host happened to be on our list.
- Three numbers change meaning on the deploy day. The error rate, because it stops counting resource failures. The error origin, because one own frame anywhere on the pageview now makes it yours, while an error whose origin cannot be determined at all stays unattributed instead of counting against a third party. And the first-party flag on each error, because of the subdomain rule. A comparison spanning that day compares two definitions.
- No backfill. Old beacons carry resource failures inside the error count and at most five error entries, so the split cannot be reconstructed from them.
Docs: Analytics → Failed resource loads, Tracker → What counts as an error.
Browser-masked cross-origin errors get their own type#
An uncaught exception in a script from another origin loaded without CORS opt-in is reported by the browser as the literal Script error., with no filename, line or error object. The tracker used to file it as a nameless Error with one empty frame, where it shared a fingerprint with every other frameless error and could not be told apart. Measured across production sites it was one sixth of all JavaScript errors.
- It arrives as the type
ScriptError. Existing rows keep their old fingerprint, so the new name applies from the next tracker load and a comparison spanning the rollout compares two groupings. - To resolve the bucket, load the script with
crossorigin="anonymous"from a host that sendsAccess-Control-Allow-Origin. The browser then reports the real error. Do not set the attribute on a host that lacks the header, because the browser refuses to run the script. - The breakdown names the hosts to look at. Where
ScriptErrorrows are present,POST /analytics/error-typesalso listsown_subdomain_hosts: hosts on your own domain, other than the page host, that served scripts or slow resources in the same scope. A hint from the hosts the beacons happened to see, not a complete list. - The error rate keeps counting them, because it counts every JavaScript error by design.
Find any organization, application or domain from one box#
The command palette (Cmd/Ctrl+K) could only offer the sites of the organization already selected. Reaching a domain in another one meant knowing which organization it was in and switching there first, and an agency had no way to type a domain and land on it.
- One search across everything you reach: your own memberships, the client organizations of every agency you belong to, and the applications and domains inside them. Each hit carries the organization it lives in and what you are there, so a click navigates straight to it.
- Applications lead the results, and picking one sets the application filter on the page you are on, the way picking a page always has. It used to drop you into that application's settings whatever you were looking at.
- Inactive sites and applications are listed and flagged. Reactivating one starts with finding it. Inactive organizations never appear.
- The same search from a script:
GET /search, withq(substring, case-insensitive),kindsto narrow, andlimit(max 50 per kind). Results come grouped per kind, best match first: exact name, then prefix, then substring, then alphabetical. A key is cut to the organizations it was issued for and to its scopes, so a key withoutsite:readgets no sites whatever its owner's role is.
Docs: API → Search, Analytics → Navigating with the command palette.
The Analytics page says what its numbers mean#
A pass over the whole page, aimed at the panels that showed a number without saying what it counted or where to go next.
- Two new panels. Active hours puts hour of day against weekday, so it is visible when something actually happens on the site. Engagement shows the share of visitors who saw a single page, with the distribution of pages per visitor and its median. Deliberately not a bounce rate: whether somebody left afterwards is not in the data.
- The checkout panel is a funnel instead of four numbers side by side: one bar per stage on a shared edge, with the share of the stage before it. Where people drop out was not visible before.
- Every breakdown panel explains itself. The title says what it counts, the footer says how many values sit behind the eight rows shown, and "Show all" leads into the Explorer carrying the page's filters, including exclusions, metric ranges and the bot switches. A single row opens there too, and clicking one adds its value to the filters instead of throwing the others away.
- The chart legend moved above the chart and switches series on and off, each on its own. With Checkouts selected, cart runs an order of magnitude above completed purchases and hid the smaller series in the shared scale. Every series now carries its own previous-period value and its own switch for it.
- New tabs: Hosts under Pages, Viewport under Devices, Campaign, Ad platform and UTM medium under Sources. The UTM tabs also say what share of pageviews carries the parameter at all, since a top list of the tagged traffic says nothing about how big that part is.
- The live strip expands. "Last 5 minutes" opens to show which pages are being viewed and which sources the visitors came through.
- Traffic quality reads as a split, first as one bar, then as clickable rows, on the dashboard as well. The Bots and Datacenter metrics are gone from the chart switcher: no query filled them and they drew a zero line.
- KPI tiles explain what they count on click, and the map, the filter chips, the Explorer and the device legend now carry the same icons per source, channel, platform, page type, device, browser and OS.
- The beta badge is gone from the page heading.
Docs: Analytics.
The map opens on your traffic, and an unfinished bucket looks unfinished#
- The map frames the countries that carry the traffic instead of a fixed view. A shop selling into DACH opens on Europe rather than on a frame stretching from the Americas to China. The frame follows pageviews rather than country count, so a single visit from the far side of the world does not pull it back out to the globe while a market holding a fifth of the traffic still does.
- The map is readable in both themes. Countries without data were painted black, because the fill was invalid CSS and SVG paints an invalid fill black. Traffic shading now has five clearly separated steps with a legend that puts numbers on them, chosen separately for light and dark, and borders follow the theme instead of a fixed black. The map also has a maximum width and a fixed two-to-one shape, so a full-width map no longer pushes everything under it off the screen.
- The bucket that is still filling looks the same in every chart: a dashed line whose dashes crawl forward, so it reads as a line still being drawn. Seven charts drew it standing still, among them the error rate and the request trend, and the percentile charts (LCP, TTFB, the Fetch/XHR trends, the synthetic trend) drew no dashed bucket at all and let the unfinished value fall to the floor at the right edge.
- The traffic chart no longer drops to the floor at its right edge. Zero-filling reached into the hour that is still running, so a five-minute-old hour arrived as a hard zero. A bucket containing the current moment is never invented: it is drawn dashed when the backend has it, and left to the hatched band when it does not.
- The Explorer downloads as CSV, next to "Copy as Markdown": same rows in the same order, values unformatted, the unit in the header, so a spreadsheet can compute with them. A bot filter now also survives the jump into the Explorer.
- The Active hours grid only shows the weekdays the selected window actually reached, instead of a full week with five empty rows that read as "nothing happened on Wednesday".
A deleted domain stayed on the applications page#
Deleting a domain now removes its row right away, instead of leaving it there until the page was reloaded. The rows are read through the query cache and the delete only refreshed the sidebar's site list, so the page went on showing what it had. Adopting a detected domain refreshes the same reads now, which keeps the domain count on the application in step as well.
Plans, and the application as the billed unit#
Self-service billing. A customer picks a plan, checks out, and pays a fixed monthly or yearly base by card or bank transfer.
- The application is what gets billed, and only a production one. A development application is free and unlimited, and the environment is switchable on the create form and in the application's settings. A plan includes a number of production slots; creating or promoting one past them is refused with
402 app_limit_reached, and the create form warns at the environment switch before the click, offering a development application or an extra slot instead. - A new organization has no plan.
planstarts atnoneand leaves it by exactly three routes: an approval starts its trial, a checkout buys a plan, or a partner provisions it. Organization creation used to hard-set a tariff nobody had chosen. The wire values arenone,beta,light,standard,partnerandenterprise;prois nowstandardandstarteris nowlight. - Capacity is bought in advance, never billed after a spike. Pageview volume comes in permanent blocks, in the increments the plan sells, and extra production slots are their own add-on. Plan, blocks and slots go into the same first payment and into the recurring amount. A monthly job snapshots each site's pageviews per period so the quota meter and the per-application usage have numbers to show.
- Every price is shown net and gross, with the VAT rate, reverse charge named where it applies. Invoices list what was booked line by line (plan, pageview blocks with their quantity, application slots) with the VAT breakdown and a PDF download.
- Billing is sold within the EU for now. A billing profile with a seat outside the EU is refused when it is saved, with the error code
billing_country_not_supported. Country, postal code and VAT id are validated against each other when the profile is saved rather than costing something silently later. - Billing has its own two permissions.
billing:readopens the invoices, the address and the VAT id and is a member permission, deliberately outside the viewer bundle, so a read-only seat does not see the Billing tab at all rather than opening it onto a row of 403s.billing:managemoves the money and stays with the owner. An agency reading its client's invoices resolves to member there, which is exactly read without manage. - Three surfaces instead of one dense tab, cut along the question: "Plan & usage" for what the organization is on and how much is in use, "Billing" for who pays and how, and a separate plans page for choosing, because the feature lists need the room.
- Trials start when an organization is approved, with the days left in the header banner and in the billing tab.
- The payment method on file is visible and replaceable, with brand, last four and expiry. It is flagged 30 days before it expires and named as expired afterwards.
- Every gated action carries its reason: a tooltip on hover plus one visible notice per page, with a jump link to whatever is incomplete. An organization without a plan is sent to plan selection rather than into a checkout for the cheapest one.
How old the cache hit was, per tier#
The cache status says whether the visitor got a cached object. Two new Server-Timing keys say how old it was, which is the other half of the question: a shop with a 95 percent hit rate whose objects average four hours old is serving fast and stale.
fm-cdn-age;desc=42fillscdn_age,fm-origin-age;desc=312fillsorigin_age, both in seconds. Emit them on a cache hit only, because on a miss the age is 0 and drags every percentile down. The value ridesdesc, notdur:duris specified in milliseconds and DevTools draws it as a bar in the document request's timing view, so an age there would render as a backend phase that never happened. Values above a year are dropped rather than clamped, since a number that large is a timestamp.- Two columns, not one. A shop behind a CDN has both ages in the same pageview. The origin's header freezes inside the CDN object, so under a CDN hit the origin value is the age at the moment the CDN fetched, and only the sum is the age the visitor saw. The Server-Timing panel reports three rows,
cdn,originandcontent(the sum under a CDN hit), computed server-side because the p75 of a sum is not the sum of the p75s. - Both are metrics with the usual avg and p50 to p99, fields on the beacon rows and in the pageview detail next to the cache status they qualify, and available through the MCP server's
get_server_timing. fm-fpcis renamed tofm-origin-cacheand the old name is no longer recognized. It was the only first-party key calling the origin tier something other thanorigin, which next tofm-origin-agewould have put two words for one tier in a single header. A header still sendingfm-fpcshows it in the panel's top-keys list instead of silently filling nothing.fm-cdnkeeps working;fm-cdn-cacheis the documented spelling.
Docs: Server-Timing → Cache age.
The pre-grant connected-app key is gone#
api_keys.kind no longer has oauth_app, and oauth_client_id is dropped with it, so two kinds remain: personal and org. The app consent flow used to end in a long-lived key owned by whoever clicked "connect"; it has ended in an OAuth grant since connected apps became OAuth connections of the organization, and the old shape was never issued in production.
GET /account/api-keysentries no longer carryoauth_client_id. Connected apps were never meant to be read from that list; they are at/account/connectionsand under the organization's connections.- Every key now audits as
api_key.issuedandapi_key.revoked. Theapp_token.*entries belong to grants alone, which is what they described on the reading side all along.
Docs: API → Authentication.
The MCP tools answer with their denominators, and their filters match#
Two rounds of feedback from MCP clients working through the catalog against a live organization. Everything here answered plausibly rather than failing, which is what makes it worth a note: nothing in the response said the question had not been asked the way the caller meant it.
- Every p75 now carries the measurement count behind it.
get_overviewgained asamplesblock,compare_periodsanddetect_changesone per period. It is not the pageview count: the vital columns are nullable, so a client quoted an LCP p75 built from seven pageviews with nothing to warn it. It also makes a null legible, sinceinp_p75_ms: nullbesidesamples.inp: 0is "nothing measured here" rather than a disabled feature. - A
urlfilter written as a path matches the path. Every tool description offers/checkout*as its example and the column stores the absolute URL, so it matched nothing on every site and answered a clean zero. - Filter values match case-insensitively, every text field of them.
browser: ["chrome"],device_type: ["Desktop"]andutm_source: "Google"each answered zero on data holding exactly what was asked for, because the stored spelling isChromebutdesktop.countryrefuses a name outright and repairs the codes ISO does not assign (UKandENtoGB,ELtoGR). get_breakdownreports the floor it applied and stops flooring counters.min_samplesdropped rows invisibly, and an empty result under a floor of 50 is a different finding from an empty dimension. It also hitpageviews, where the value is the sample count, so "top pages by traffic" on a small site returned one row and dropped nine.get_error_detailstops inventing a signature. An aggregate over no rows returned typeErrorwith 1970-01-01 timestamps; responses now carryfound, and a false one nulls the signature. A tag no site carries is refused rather than answered as a scope of zero sites.- The server instructions now state that there is no per-site timezone, since a period without an offset is read as UTC and a client had been guessing
+02:00.
Connect your own AI client over MCP#
An organization's real-user data can now be read by an AI client you run yourself (Claude Desktop, Claude.ai, Cursor) over the Model Context Protocol: 19 read-only tools serving the same numbers the dashboard shows.
- Off until an owner turns it on, under Organization settings → MCP server. While it is off, nobody can approve a connection to the organization and a token approved earlier is refused with
403 mcp_disabled. It is its own consent decision with its own audit entry, because the model behind your client belongs to a provider we neither choose nor contract with. - One address for everybody:
https://api.fastmon.eu/mcp, shown with a copy button once the switch is on. Add it in your client as a remote MCP server or connector; it sends you back here to sign in and approve. There is no key to paste and no secret to store. - You pick the organization while approving, not in the address. A connection reads exactly the one you picked, so somebody who works in two connects twice. The consent screen says that this connection is an MCP one, and an organization with the server switched off cannot be approved for it.
- The connection belongs to you, not to the organization. What it may do is re-derived from your current role on every request, so it narrows when you are demoted and ends with your membership. Manage it under Account → Access.
- Nothing writes. Traffic and Web Vitals, breakdowns and time series, errors, LoAF and resource attribution, Server-Timing phases, Lighthouse results. Periods are capped at 90 days, breakdowns at 50 rows, and requests at 120 per minute per person. The three Lighthouse tools also need
synthetic:readand the organization's Synthetic Monitoring opt-in. - Switching MCP off is not a way to cut somebody off. It closes the MCP endpoint; a connection already approved keeps reading through the ordinary API, which is all it ever asked for. Ending one is the member's move, or offboarding.
- Agencies connect personally. A partner can connect a client organization, and the access ends when the partnership does. The switch itself stays with the customer, so onboarding costs them one click.
Docs: MCP server.
An app gets the permissions it asked for, and a token bound to one endpoint#
Several changes to how an integration connects, all of which narrow what a connection ends up carrying.
/auth/app/authorizetakesscope. A client can ask for less than it declared at registration, and the consent screen shows exactly that. Send no parameter and the screen offers the full declaration as before. Scope names we do not implement (openid,offline_access) are dropped; a request where nothing survives comes back asinvalid_scope.- A registration that names no scope declares
analytics:read, instead of the full permission catalog. Existing registrations keep what they have. Read thescopein the response to see what you got. - A registration that names no
credential_ownergets a personal connection, which used to be an organization-owned one. Declareorganizationexplicitly where the connection has to outlive the person; a shop plugin already does. - A token can name the endpoint it is for (
resource, RFC 8707), on the authorization request and again on the token request. The consent screen reads that value, shows what kind of connection it makes, and hands it back untouched; the endpoint then refuses a token addressed elsewhere, and one addressed nowhere. Grants approved before this cannot be addressed until they are approved again. - Fixed: an agency saw only its own organization on the consent screen. The client organizations of a partnership are offered now, and belonging to two agency organizations no longer ends the screen in an error.
Docs: API → OAuth apps.
The Explorer's table is one screen with one scrollbar#
The column labels were meant to stay put while the rows move under them, and they did not. The table owns the vertical scroll now, so the header sticks without the table escaping its card.
- One scroll area on the page. From tablet width up, the page itself stops scrolling and hands whatever height is left to the table. Below that nothing changes, because there is not enough height to divide.
- Maximize sits at the right end of the line heading the result and trades page chrome for rows: the page heading, the Hits strip and the drill-down hint fold away, the query panel and the result filters stay. The choice is remembered per person rather than put in the URL, since it is how you read the page and not part of the query a shared link is about.
The Explorer's column filter closed in the frame it opened#
Picking Filter on a column header put the little form on screen and took it away again immediately, from the funnel in the aggregated table as well as from the "..." menu over the raw pageview list. Every route into that popover hands focus to it from outside, and it was reading that as a dismissal. It no longer treats focus as a dismissal at all; a click outside, Escape and its own buttons still close it.
Origin host and logged-in, on the Server-Timing page#
The two dimensions promoted from fm-host and fm-loggedin now have a dashboard to read them in. Both are group-by, filter and column choices wherever the other categorical dimensions are, and both ride along on the beacon row and on the visitor journey.
- The request path is cut both ways at once. The cache status says where a request stopped, the login state says why it stopped there. Both are on the card as partitions of their own, side by side where the width allows, with the pageview count beside each share.
- A cluster gets a verdict, not just a table. A panel names the host running at least half again the median of its peers, with the numbers it got there by, and says nothing when the spread is ordinary. It stays quiet below three hosts and states what share of pageviews reported no host at all.
- The phase trend splits by host or by login. The same chart with one line per value instead of one per phase, so a box that drifts away from its peers drifts here with the day it started. Which cut is on screen lives in the URL.
- "Not reported" is a bucket with a name. On a half-instrumented cluster that group is one you have to be able to see, not missing data. A third state of your own (
b2b) is shown as stored rather than mapped onto yes or no.
August 2026
Server-Timing: which server rendered it, and was it a logged-in render#
Two new Server-Timing dimensions, both opt-in via the first-party fm-* key (no vendor aliases, because a bare host or user from an unknown framework could mean anything). Both are filterable and groupable and appear on the beacon row and the visitor journey.
fm-host;desc=web-03becomesorigin_host: the origin node that rendered the pageview. When one node of five runs hot, the aggregate backend time only shows a drifted p95; the per-node split names the box. Send the short, stable hostname: values over 32 characters are dropped, not truncated (a truncated hostname would be a wrong node name).fm-loggedin;desc=nobecomeslogged_in: whether the render was personalized. Authenticated requests bypass the full-page cache by design; without the flag they sit in the same average as the cacheable traffic and every cache-hit-rate number is quietly wrong. Values fold ontoyes/no; a slug-shaped third state of your own (b2b) passes through.- The coarse flag only: no customer number, group ID, role, or username in
fm-loggedin, and no request or session IDs infm-host. The collector enforces the shape; the intent is your side of the contract. - Fixed alongside: a reserved key with a numeric label (
fm-loggedin;desc=1,pagetype;desc=404) was misread as a duration and blanked the dimension it was sent for.
API keys the organization owns#
Every key used to belong to the person who created it, so a pipeline's key died with the account that made it. An organization can now own keys directly.
- Create under Organization settings → Access or with
POST /v1/organizations/{org_id}/api-keys, behind the neworg_key:managepermission (members and owners, not viewers, never partner access). The secret starts withfmo_and is shown once. - The scopes are the whole authority. An organization key has no owner whose role could cut it, so it does exactly what it was issued for. The ceiling at creation is what the person issuing holds in that organization; no key can ever carry
org_key:manage. - Offboarding changes nothing. The key keeps working when the person who created it leaves. Anyone with
org_key:managecan revoke any key on the list, without re-authentication. - Agencies get one key for all clients. A partner organization can issue a key with
audience: "clients": it acts in whatever organizations are its clients right now, resolved on every request and capped at the member bundle. - The audit log names the actor. Issuing and revoking record the person who clicked; what a key or a connected app does later is recorded under its own name.
Docs: API → Authentication.
Connected apps become OAuth connections of the organization#
A connected app used to hold an API key issued to whoever clicked "Connect". It now holds an OAuth connection that belongs to the organization and survives every account.
- Short-lived tokens instead of a key. The app authenticates with an access token that lives 15 minutes and renews it with a rotating refresh token. A refresh token works exactly once; a replayed one disconnects the app, so a stolen copy cannot be used quietly.
- The consent screen states what is approved: which account is approving (with the option to switch), which organization the connection binds to (real memberships only), and the requested permissions with descriptions, narrowable before approving.
- Disconnect under Organization settings → Access or with
DELETE /v1/organizations/{org_id}/connections/{id}. Access tokens already issued expire on their own within minutes. Approving the same app again updates the existing connection rather than adding a second one. - A connection can belong to one person instead when the integration declares it (an assistant that reads on your behalf, for example). Its authority follows your current role on every request and it ends with your membership; manage it under Account → Access.
- For app developers: self-registration, authorization code with PKCE, discovery under
/.well-known/oauth-authorization-server. Connections made before this change keep their key until you reconnect.
Docs: API → OAuth apps.
API keys can be edited and rotated#
Adding one permission to a key used to mean a new secret in every pipeline that uses it. Keys now change in place, personal and organization keys alike.
- Edit name, scopes, and expiry with
PATCH. Your password is asked only when the change widens the key (a new scope, a later expiry); narrowing and renaming pass without it. - Rotate (
POST …/rotate) issues a successor with the same name, scopes, and organizations and a fresh secret. An optional grace window (up to 24 hours) keeps the old secret working until a deploy has reached every consumer. Rotating never extends the expiry. - An organization key above your grade is read-only. Editing and rotating need someone who holds everything the key holds; revoking never waits for that.
- The key list answers "can this act right now". A status says active, expires soon, expired, or revoked; an expired key no longer sits among the live ones.
Docs: API → Authentication.
The API drops its /v1 prefix and keeps answering under it#
Endpoints now sit directly under https://api.fastmon.eu: GET /organizations, POST /sites/{site_id}/releases. The base URL in the docs and every example follow.
- Nothing has to move. Every path also answers under a leading
/v1, permanently, so a pipeline or plugin built against the old URLs keeps working untouched. Leave the prefix off in new code. - The OpenAPI document is at
/openapi.json, and the reference here is generated from it, so the paths you read are the ones without the prefix. - Public ingest (
POST /c/{collector_hash},GET /s/{source_hash}.js) never carried a prefix and is unchanged.
Docs: API → Base URL.
Roles are bundles of permissions, and a read-only viewer joins#
Access control moves from "is this user an owner" to named permissions (resource:action), and a third role joins.
- Viewer: a read-only seat. Sees dashboards, analytics, releases, synthetic results, notification rules, and members; changes nothing and triggers nothing that costs money or sends mail. Assign it on invite or under Members.
- Permissions are what routes check and what the dashboard gates actions on. The organization payload carries your
roleandpermissions;GET /v1/permissionslists the catalog. - Several owners are possible.
PATCH /v1/members/{member_id}changes a role; the last owner cannot be demoted or leave while other members remain (last_owner). - One denial code:
403 permission_deniedwith the missing permission indetails.permissionreplacesowner_required. - Adding a member directly (
POST /organizations/{id}/members) now needs an owner (member:manage); members invite instead. - The proxy secret's value is shown only with
app:write; a viewer sees that one is set (proxy_secret_redacted).
Docs: Organizations → Roles.
API keys: scoped, bound to organizations, with an expiry#
The personal API token is replaced by API keys that say what they may do and where.
- Create under Account → API keys or with
POST /v1/account/api-keys: name, at least one organization, at least one scope, and an expiry (or never). Creating asks for your password and sends a security mail; the secret is shown once. - Scopes are permissions and are intersected with your current role on every request, so a key can only ever narrow you.
GET /v1/permissions/selfshows what a key can do per organization. - Expiry and last use: an expired key answers
401 credential_expired;last_used_atsays whether anything still uses a key. - Revoked keys stay listed with
?include_revoked=true. Revoking needs no re-authentication. - Session only: creating and revoking keys, passkeys, 2FA, password, e-mail, creating an organization, accepting an invite, and the
/adminroutes refuse API keys. - Removed:
GET/POST /v1/account/api-key, and registration no longer hands out a key. Connected apps receive keys with your permissions and organizations as of consent.
Docs: API → Authentication.
Create the release key from the CI/CD snippet#
The CI/CD snippet on an application creates its key in place: name prefilled, expiry your choice, and after the password prompt the secret is written straight into the curl command. The key carries release:write in that organization and nothing else. Under Account → API keys, a key names its organizations first, groups its scopes by resource, and revoked keys keep a section of their own. A permission denial now names the permission to ask for, and an audit-log entry expands to its full details.
Server-Timing: cache_dur folds into kv_dur, external_dur becomes http_dur#
The cache/KV split keyed on the vendor: a Redis object cache counted as KV, the same cache on memcache as Cache. Eight phases remain.
kv_durnow sumsredis,valkey,kv,fm-kv,memcache, andcache; the dashboard labels it "KV / Cache".apculeaves the sums (drill-down only).http_durreplacesexternal_durwith the same members and the aliasfm-http.fm-cacheandfm-externalare dropped; no site ever sent them.mongodbandsqliteadd intodb_duron top of the relational alias: where a document store appears, it is the primary datastore.countper phase onPOST /analytics/server-timing: the number of pageviews behind each percentile.queueandtwigjoin the catalog (no custom slot used). Stored values are not rewritten; notification rules naming the old metrics keep evaluating on the new ones.
Shorter tooltips in the response breakdown#
A tooltip in the response breakdown is now the row's name, its start and end (61ms → 358ms), and the share of requests that reported it, capped at a readable width. The remaining prose appears only where a bar cannot say it: when the Early Hints head start moved the bar off its metric. Pointing at a server phase shows that phase's tooltip, not TTFB's. "Try again" on an error page re-runs the loaders instead of re-rendering the same failure.
The Early Hints head start and the store phases in the dashboard#
The dashboard picks up ttfb_final and the Search and KV phases.
- Response breakdown: on sites with 103 Early Hints, an "Early Hints" row runs from the first byte to the final response headers. The server phases end there, and the transfer bar starts there instead of counting the head start twice. A server phase longer than the first byte keeps its reported length instead of being squeezed into the wait.
- TTFB page: an Early Hints panel with the head start at the selected percentile, both first bytes as one split bar, and the share of pageviews it was measured on. Shown only where the two values differ.
- Search and KV are phases of their own on the Server-Timing page, in the waterfall, and as Explorer metrics and columns. The "Backend phases" preset gains a DB-against-Search-and-KV chart.
- Pageview drawer: the Server-Timing composition is a nested list (each part under what contains it, with its share of that row) and unattributed time is its own row. It no longer adds the backend to its own parts.
- Every phase has its own color in the breakdown, and the cache phase is called "Cache" instead of "Cache (FPC)": it sums the application's cache calls; the full page cache is the origin cache status.
Docs: TTFB → Early Hints.
The visitor journey says who the visitor was, and hands the ID over#
Two things a shared ?visitor= link was missing.
- Who it was: the journey header shows country, device, OS, and browser, read per pageview. What varies across a stitched visitor is listed rather than collapsed.
- The ID: click-to-copy in full on hover. A row action narrows the page to that visitor: a filter chip for a stitch (read by the other analytics pages too), the search box for a session ID.
- Device, OS, country, and browser get the same hover plus and minus as the Explorer's pageview table.
The page classes people recognize keep their color#
Yesterday's per-class colors on the visitor journey hashed every non-funnel page type onto a nine-hue wheel, and collisions were routine: "category" and "product" hash to the same hue, so whichever appeared first kept it and the other moved, which made "category" teal in one visit and something else in the next. The nine classes almost every site has (home, category, product, search, blog, account, contact, wishlist, static) own a hue outright now, with the German spellings reading as the same class; site-specific names keep their hashed hue and give way when they land on a taken one. "Artikel" is deliberately not in the list: it is a product on one shop and a blog post on the next.
The organization overview is about applications#
The overview listed bare sites, which hid the structure exactly where a new user first meets it: an application carries the tracker and its collection settings, a site is one of the domains underneath it. Applications are the cards now, busiest first, each listing its domains with the pageviews and the LCP of the last 24 hours next to the status dot. Above them the same window for the whole organization: pageviews, visitors, LCP p75. An application without a domain says so and offers to add one instead of being invisible.
Search engines and key-value stores get their own Server-Timing phases#
cache_dur counted redis and Shopify's edge_cart as cache time, and Elasticsearch counted nowhere. Two new phases fix that.
search_dursumselasticsearch,opensearch,solr,search, andfm-search.kv_dursumsredis,valkey,kv, andfm-kv.- Both are percentile phases on
POST /analytics/server-timing, row fields on/analytics/beacons, and aggregatable metrics.db_durstays purely relational. cache_durchanges meaning:redismoves tokv_dur,edge_cartleaves the sums. Stored values are not rewritten, so a long-windowcache_durchart steps down at the deploy. If Redis is your cache, emitfm-cacheon top.- Shopify hygiene:
compressionlevelandspeculationtagsare dropped (configuration, not timing),compressiontimejoins the catalog, and a numericdescoutside the duration window (a theme id) stays a description instead of becoming a six-year duration.
ttfb_final: the real first byte behind 103 Early Hints#
Behind 103 Early Hints (Shopify storefronts, Cloudflare) TTFB read below the server's own Server-Timing phases. Both were right: responseStart is the first byte of the interim 103, while the Server-Timing header rides on the final response.
- The tracker reads
finalResponseHeadersStart(wire keyfrs, Chromium 115 and later,nullelsewhere), with TTFB's activation adjustment and 60-second cap. The collector stores it asttfb_finaland drops values belowttfb. - Reading the pair: empty means the browser does not report it, or a soft navigation or bfcache restore; equal to
ttfbmeans no interim response; abovettfbmeans Early Hints, and the difference is the edge's head start. - TTFB stays
responseStart, so it keeps comparing to CrUX.ttfb_finalis a row field on/analytics/beacons, part of the visitor pageview detail, and an aggregatable metric. - The tracker's Server-Timing denylist gains Shopify's
compressionlevelandspeculationtags, mirroring the collector.
Docs: TTFB → Early Hints.
The visitor detail carries each pageview's client context#
The visitor panel could not say which device, browser, or country a pageview came from without the visitors list open next to it. Each pageview in POST /analytics/visitors/detail now carries country, browser, browser_version, device_type, and os. Per pageview rather than per visitor on purpose: a stitch visitor can span devices, countries, and browsers, and collapsing that to one value would state something the data does not say. No new collection; these are the same fields every beacon row already stores.
The checkout in the Explorer#
The funnel stage and the cart counters were readable on the Analytics page only.
- Group by Checkout Stage: stacked over time, one line per stage for any metric, a stage column, drill-down per row. The empty stage is "No checkout".
- Cart Actions and Failed Cart Actions as metrics, chartable, summable, and usable in notification rules. They undercount by construction and are never the denominator of a rate.
- Filters: "Cart Actions at least 1";
checkout_stagecan be excluded as well as filtered. - Per pageview: stage and cart actions as table columns, and a Checkout section in the drawer.
- A "Checkout" preset, scoped to the three stages. The dashboard's checkout panel links into the Explorer with the same window and site.
- API:
checkout_stageas group-by and exclude,cart_interactionsandcart_interaction_errorsas metrics and metric ranges, all on/analytics/beaconsrows too.
Comparison buckets are paired by clock, not by list position#
A previous-period comparison with a time-only grouping matched the two result lists by index. ClickHouse never returns an empty bucket, and the buckets are empty at different places in the two windows, so a single quiet minute in the previous period shifted every comparison after it by a minute: the chart compared 14:03 against 14:02 and called the difference a change. At minute granularity empty buckets are the normal case. Buckets are now keyed by their own timestamp, shifted by the distance between the two windows; a bucket with no counterpart reports no comparison instead of borrowing its neighbor's. Month keeps positional matching, since a calendar month has no fixed width.
The time picker reaches below six hours#
The quick ranges gain Last 15 minutes, Last hour, and Last 3 hours, all bucketed by minute, so "is something burning right now" no longer requires typing a range by hand. The digit shortcuts cover all eight presets, 1 for 15 minutes through 8 for 90 days, and a ?period=15min link keeps meaning the same live window it did when shared. The range panel sorts its quick ranges into two columns, minutes and hours on the left, days on the right with Today and Yesterday on top, and the recently used ranges below as a grid of at most four. Opening the panel no longer drops focus into the From field, so the digit and t/y shortcuts work while it is open.
Each page type wears its own color on the visitor journey#
The Type chip drew every page class in the same green, so a visit full of home, product, and search pages read as one undifferentiated rail. The value now carries the color: a value that is also a checkout-funnel stage takes that stage's color so the two chips next to each other agree, a value that means "could not tell" is gray like everywhere else, and every other class gets a stable hue from a fixed wheel, walked forward within one visit so two classes on the same screen never look alike. The wheel skips red, which on the visit row already means error.
Every page names itself in the browser tab#
The title was one static "fastmon" for the whole app, so two open dashboard tabs were indistinguishable. Every route now sets its own title, most specific part first so truncation keeps the distinguishing bit ("LCP - Core Web Vitals - fastmon", "Table - Explorer - fastmon"), translated with the active locale and using the same words as the sidebar and breadcrumbs.
Checkout funnel: cart, checkout, and success as observed facts#
Shops could see how fast their pages are, but not whether visitors reach the checkout. Now they can, with no tracker change and nothing new collected.
- Stages from the URL: a pageview on a cart, checkout, or thank-you page carries that stage, matched at ingest against built-in patterns for Shopware 5 and 6, WooCommerce, Magento 2, PrestaShop, JTL 5, and Shopify.
- Cart interactions from fetch/XHR: a first-party
POSTto a known cart endpoint marks the pageview, with the kind where the endpoint reveals it. - Dashboard: Checkouts as the fourth headline metric, a checkout panel with visitors per stage and cart requests by kind (replaces Tech insights), the stages on the current-visitors card, and per-minute averages on the count tiles.
- Filters, Visitors, Business Impact:
checkout_stageas a filter dimension, the Visitors list filtered to a stage or a cart interaction, and a measured conversion rate on Business Impact where the funnel has data. - Facts only: no abandonment metric, and success counts thank-you pages, not payments.
- Settings: on by default, opt-out and per-stage paths under Settings → Checkout funnel. Needs
store_stitch; cart interactions needcollect_fetch_xhr. - API:
POST /analytics/funnels/checkoutand/journeys;contains_checkout_stageandcontains_cart_interactionon the visitors list.
Docs: Checkout funnel.
Switch a filter off without deleting it#
Answering "what does this look like without the country filter" meant deleting the filter and typing it back in.
- Every chip has an on/off switch. Off, it keeps its values, stays in place struck through, and the query runs without it. Works for includes, excludes, metric ranges, and boolean filters.
- Off means off everywhere: not counted in the badge, no country drawn as picked on the map, device segments unpicked. Clicking one of them switches the filter back on rather than adding a second.
- In the URL as a leading
!on the entry's key, so older links open with everything on. - The scope line shows a country by its flag instead of its code.
Visitors: a summary that describes the period, and links that stay stable#
The summary above the Visitors list was computed over the loaded rows, so it moved when you paged forward.
- Summary over the period: visitors, visitors with a single page, and the median pages per visitor. Average duration is gone rather than repaired: there is no window-wide visit duration to compute it from.
- Country shows its flag and localized name, the code on hover.
- URL: behavior filters are plain numbers (
?minPages=3), and the deep-link parameter isvisitorinstead ofsession. Old links keep working and rewrite themselves. - Fixes: an old
/sessionslink with a page-type filter arrived unfiltered, and a 32-digit visitor id that reached the app as a number had lost digits and was passed on anyway. It is now dropped instead of corrupted.
Response breakdown: where a typical response spends its time#
The Network section gets a full-width response breakdown card. It replaces the Network Request Timing chart, which showed four metrics as one bar with nothing to drill into.
- A waterfall at the selected percentile, the way devtools draw it: DNS, connect, wait, and transfer as a staircase, then the LCP phases (load delay, load duration, render delay). Server-Timing phases nest inside the wait; the CDN and origin cache comparisons split their bars into hit and rest. FCP, LCP, and DOM Interactive sit as markers on the axis.
- Honest math: percentiles of phases do not add up, so the wait is derived as the remainder and the LCP phases scale to end at the LCP marker. Tooltips show the raw value, its position, and the share of requests reporting the phase.
- Rows below list page types (where reported) or pages with compact bars. Clicking a row scopes the waterfall ("Overall › cart"); the selection is in the URL.
- The pageview drawer in the Explorer shows the same waterfall for one pageview.
- Fix: a deep link to a card further down the page (
?panel=) did not scroll, because the section had not rendered yet. It loads immediately now.
Top pages and page types in one performance table#
The Top Pages card listed views and LCP, and page type performance had no place at all, although a slow checkout hides behind a fast home page in site-wide numbers.
- A full-width table: views with a share-of-traffic bar, then LCP, INP, CLS, and TTFB as status-colored pills at the chosen percentile.
- Page types or pages, switched in the header. Sites with page type detection open on page types, with untyped traffic as a muted row so the shares add up; sites without it land on pages and are pointed to the settings.
- Rows still filter the dashboard or drill into the Explorer. The traffic trend gets its own full row again.
- Large counts abbreviate the same way everywhere (K/M), with the exact number in the tooltip.
First-party proxies can forward the real visitor IP#
Relaying beacons through your own server collapsed every visitor into one: all requests came from your proxy's IP, and trusting any X-Forwarded-For would allow spoofing.
- An explicit grant per application: generate a proxy secret under Settings → Collector, send it in the
FM-Proxy-Keyheader on the beacon route, and the edge reads the real visitor IP from your proxy'sX-Forwarded-For. Geo and visitor counts work as if the beacon had arrived directly. - Rotation without a gap: the previous secret stays valid until the next rotation. Deleting the secret switches the trust off.
- API:
POSTandDELETE /v1/applications/{id}/proxy-secret.
Docs: First-party proxy guide, with nginx, Caddy, and Cloudflare Worker snippets.
Shopware 6 page types: home counts as home, and detection survives 6.8#
Two changes to the shopware6 ruleset.
- Home counts as home. It counted as
category, because Shopware 6 renders it through the navigation controller and the expectedis-ctl-indexclass does not exist. The tracker now reads controller and action class together (is-ctl-navigation is-act-home). - Detection survives 6.8, which removes the
is-ctl-*andis-act-*classes. The tracker prefers theis-active-route-*class shops emit since about 6.6. On it the checkout splits properly: the cart page ascart, the confirm, finish, and register steps ascheckout, the account area asaccount. Older shops fall back to the legacy classes, where cart and checkout sharecart. - A browser-cached tracker keeps its old behavior until the cache rolls over.
Docs: Tracker → ruleset table.
Filter work: the world map, the header chips, and the value editor#
A round of filter work.
- World map: clicking a country works and opens a popover with its numbers and the + and - actions (filter or exclude). Excluded countries get a red outline, and the map keeps zoom and pan during a refetch.
- Header chips: a chip opens straight into its value editor, an x removes the filter, hovering lists every selected value with its own x.
- Value editor: selected values are pinned at the top and stay removable even when they match no traffic; including and excluding the same value can no longer coexist.
- No more shaking: the performance tiles, the geo map, and the Core Web Vitals card keep the previous values while new ones load.
The Explorer table sorts on the server#
A header click reordered only the rows already loaded, so "the ten worst TTFB pages" meant "the worst among the busiest".
- Server-side sort: the backend sorts before it truncates, and the sort travels in the URL.
- Nothing sorts implicitly: selecting a metric adds a column and nothing else; without a chosen sort the table states the backend's default order.
- The raw pageview list sorts server-side too, by time or any column, empty values last. Its visible columns are part of the link (
?cols=). - Metric columns moved from the "Columns" menu up to the query panel.
- API:
/analytics/beaconsacceptsorder_byas{field, direction}over any scalar row field.
Server-Timing labels: keys that carry a value instead of a duration#
An origin can send a Server-Timing key as a label instead of a timing (pop;desc="DE-1331"). Those keys were stored but invisible, because everything ranked by milliseconds.
- Visible now: the Server-Timing page lists them below the timed keys with the mix of their values, the pageview drawer shows them as key/value fields, and the Explorer shows any custom key as a column and sorts and filters on it.
- Scoped pageview list:
/analytics/beaconsgains aserver_timingfilter for a key or one of its values. - PoP keys are kept:
pop,fastly-pop,cdn-pop, andx-qc-popname the CDN node, not the visitor (asn,country, andipv6stay blocked). - Fix: expanding the trend of a custom key with a hyphen or dot in its name (
wp-total) returned an error.
Copy a table as Markdown#
The aggregated Explorer table and the raw pageview list each get a copy button that puts the table on the clipboard as Markdown, exactly as shown: the filtered rows in the visible sort order, with the formatted display values and the chosen columns. Handy for a ticket, a chat, or a prompt. Values containing pipes or line breaks are escaped, so a copied row cannot break the Markdown grid.
Duplicate rows, overflowing card headers, and silent zero tooltips#
Three small fixes. The raw pageview table could duplicate or drop a row on sites that store no session IDs, because two pageviews in the same millisecond shared a row key (it keys on the pageview id now). A card header carrying controls could push its own buttons past the card's edge. And a chart tooltip showed nothing for a value of exactly 0.
The Explorer filters like every other page#
The Explorer had a filter apparatus of its own, right next to the standard filter menu that does the same job everywhere else.
- Standard controls take over: query filters in the scope menu, presets in one menu with saved queries, client-side result filters above the table.
- Refresh and live mode move into the time-range control.
- The filter count counts every kind: an exclude, a metric range like
lcp >= 2.5s, or the bot switch used to be active while the button read as nothing filtered.
UI optimizations, especially on mobile and around filters#
A round of interface work across the signed-in app, focused on mobile and filtering.
- Header in two halves: where you are on the left, what you filter on the right. Time range, percentile, device, and filters fold into one scope summary (
Last 7 days · p75 · 📱 · Country: DE +4) with the controls in a popover. Active filters sort by importance. - Mobile: the sidebar becomes an off-canvas drawer, dialogs stay within the screen, headings and spacing are consistent across pages.
- Filter editor fixes: a single-valued filter shows its current value, value counts respect the filters already applied, and the "Hide bots" switch no longer appears while editing an unrelated filter.
- The documentation, feedback, and support links collapse into one Help button.
July 2026
Sign-in, email verification, and organization switching#
A batch of sign-in and account fixes. The email verification link no longer reports a failure after it actually worked: a mail client that prefetches the link, or a quick double-tap, used to spend the single-use token and leave you on an error, and the page now offers a login button when the address is already confirmed. Registering in German sends the verification email in German instead of English. Signing in no longer bounces between /login and the dashboard in a redirect loop, and it returns you to the page you were headed for. And picking an organization while on a global page no longer drops the selection.
See and hide bots, datacenters, and automated traffic#
Every request now carries a traffic verdict.
- Human, Likely bot, Bot. Bot is a declared or verified crawler. Likely bot is a datacenter or VPN address, or a user-agent anomaly, without a bot declaration (a visitor on a VPN lands here). Human is everything else.
- Hide bots drops the whole view to real visitors in one click. A Bots & visitors chart splits traffic by verdict and lists the top bots and bot types.
- New filters: datacenter or hosting network, bot name, bot type, user-agent anomaly.
- Detection runs at the edge against a fixed list of providers and crawlers; IP and user-agent are discarded right after.
Traffic & Visitors, a rebuilt Top Pages, and a clickable map#
The dashboard groups the traffic trend, top pages, devices and browsers, and the new bots breakdown into one Traffic & Visitors area, with the traffic trend and Top Pages sharing a row so the page list no longer dominates. Top Pages is rebuilt as a compact table (page, views, LCP) with a per-row traffic-share bar and LCP colored by status, and it now shows query strings: /?q=shoes and / are separate rows instead of collapsing into one confusing pile. On the world map you can switch the metric between LCP, FCP, and TTFB (p75), and clicking a country filters the whole dashboard to it (click again to clear).
A refreshed look: new logo, sidebar, and styling#
The dashboard has a visual refresh. The sidebar carries the new fastmon logo, slides in and out instead of fading, and collapses to an icon rail with popovers and tooltips. Buttons are now pills, cards and inputs are flatter with lighter borders and rounder corners, the date-range picker and the other chart controls share one segmented style, and headings use a new serif display face. The light and dark palettes were retuned for contrast.
Fetch/XHR endpoints group tighter, and campaign parameters stay out#
The collector templates fetch/XHR endpoint paths more aggressively, so the per-endpoint view stays readable. It runs server-side, so already collected data is cleaned up too.
- Tighter templating: long content slugs collapse to
:slug, soft-navigation document fetches (_rsc,_data,/_next/data/,/page-data/) to/:page, third-party hosts lose the query string, and eight or more query parameters show as?:many. - Ad-attribution parameters stay out (
gclid,gbraid,wbraid,msclkid,fbclid, theutm_*andgad_*families), on endpoint paths and page URLs, even withcollect_query_keyson. Naming one incollect_query_values_forkeeps it.
Docs: Tracker → collect_query_keys.
Redesigned sign-in pages#
Login, registration, and password reset move onto a single centered card over a plain background, with a new serif display face for the headings and the theme toggle in the corner. The old split-panel layout, the testimonial carousel, and the performance-risk copy alongside the form are gone.
Open registration replaces the waitlist#
Signing up no longer needs a waitlist invite: the "Get access" links go straight to registration, and anyone can create an account. A newly created organization waits for approval before it can add applications; until then the Applications page shows an awaiting-approval note, while your account and any existing data are unaffected.
Empty charts say what's actually missing#
A chart with nothing to show used to read "No data available", which described our storage rather than your site. Visitor-based charts (pages, browsers, devices, countries, referrers, Web Vitals, errors) now say "No visitors in the selected period", and request-based ones (cache hit rate, backend time, requests) say "No requests in the selected period". The diagonal "no data" hatching in trend charts is gone too: a quiet stretch is normal, so the line simply ends where the data ends and picks back up where it resumes, instead of being shaded as if something were broken.
Applications: one tracking snippet across one or more sites#
Sites and cross-domain trackers are replaced by applications.
- One application, one snippet: it owns the embed, the two hashes, and every tracker setting, and serves one domain or twenty. Each domain stays a site with its own dashboard. No more "single vs multi" mode.
- Applications page: a card per app with its sites and live status, releases, embed snippet, and analytics. Adopt, move, or delete sites inline.
- Suggested domains: a domain that sends beacons unregistered shows up to adopt in one click. The new Unknown domains setting decides: suggest (default), create automatically, or ignore.
- Defaults per application, overridable per site: retention, visitor signal, and the visitor-counting identifier. The collector is picked when you create the application; applications can be marked production or development.
- Renamed settings:
store_sessioniscollect_sessions,preconsent_levelissession_consent(deferredorimmediate), and the error, query, and fetch/XHR switches gain acollect_prefix.
Docs: Applications, Sites, Tracker settings.
Reorder, resize, and hide columns on the Explorer's table#
The Explorer's raw-requests table now lets you reorder columns by dragging a column title, resize them by dragging a header's right edge, and filter or remove a column from its own "…" menu. All three preferences persist per browser. The Page (URL) column is resizable too: it fills the remaining width by default, and once you drag it, it pins and truncates so it can shrink past its content instead of snapping back. A new auto-fit button in the toolbar resets every custom width so the table fits the viewport again with no horizontal scrolling.
'Hits over time' collapsed to a single point at minute resolution#
On the Explorer's table page the Hits over time strip rendered as a single point at minute granularity (the 6h preset and short custom windows). The bucket label doubles as the chart's x-axis key, and the minute case fell through to a day-and-month-only label, so every bucket on the same day shared one label and all points stacked onto the same position. Minute buckets now include the time of day, like the other trend charts.
Sticky save bar, breadcrumbs, and a dashboard dead end#
The save bar on the application and site settings pages now sits flush against the bottom edge, instead of floating above it with settings scrolling underneath. Breadcrumbs on those pages show the application's or site's actual name rather than a generic "Settings", and the back arrow navigates up to the parent page instead of acting like browser Back. A user who belongs to no organization no longer lands on a dashboard that spins forever with no way out; they get a create-organization state. The dashboard's live rate also stopped rendering as 0.30000000000000004/min.
Beacon Inspector: a DevTools extension for reading beacons#
A browser DevTools extension that reads the beacons your page sends and decodes them live.
- Groups beacons by pageview, unpacks the wire keys into readable fields, tracks the lifecycle from
initto the terminal beacon, and shows the Web Vitals as they settle. - Captures the
fetchbeacons and thesendBeaconbodies the Network panel cannot show. - Follow mode pins the newest pageview, Compare puts two side by side, and export copies a beacon as Markdown (optionally anonymized).
- Open source; no beacon data leaves the device.
Docs: Beacon Inspector.
Emails in German, and a note when a site goes live#
Transactional emails now go out in German or English, following the language on your account, with English as the fallback; verify-email, password reset, and passkey-added are localized today. And when a site goes live, every member of the owning organization gets a "site is live" email in their own language, linking straight to that site's dashboard.
Theme and colorblind mode follow you across devices#
Your theme (light, dark, or auto) and the red-green colorblind palette toggle are now stored on your account instead of only in one browser, so a change on one device carries over to the next. The choice is applied instantly and saved locally first, then synced in the background, so it keeps working offline or when the sync fails; on login, the preference stored on your account wins.
Fewer, better-timed beacons per pageview#
The tracker sends fewer requests and records more accurate loading metrics. No change to the wire format or your setup.
initfires immediately as a pageview-exists marker, so a bot or an abruptly closed page still registers.loadedwaits for the final LCP (first interaction, tab hidden, or a 10-second cap) instead of the first, often provisional, entry.- Update beacons only on a meaningful signal (a new first-party endpoint, a failed request, a call over 500 ms). A typical pageview sends two to four beacons.
- Terminal beacons keep
keepalive, live-page beacons no longer do, so the final INP and CLS reliably arrive on Chromium.
Docs: Beacon → fine-tuning.
Soft-navigation LCP is now measured on Chromium#
Soft-navigation (SPA route change) pageviews shipped no LCP at all on current Chrome, even when the route clearly rendered a large image, while FCP and INP came through fine. A shape change in Chrome's interaction-contentful-paint entries meant the tracker was reading the interaction (click) time instead of the paint time, giving an LCP of zero that the sanity guard then dropped. The tracker now reads the paint time and matches each paint to its route by interaction id, so SPA route LCP is populated.
A tag included twice no longer doubles a pageview#
If the tracking tag was included or executed twice on a page (a common tag-manager mistake), each copy wrapped the browser's history API again and registered its own listeners, so a single SPA route change fired in every copy and produced duplicate pageviews and doubled beacons. The tracker now guards against a second execution and bails out, so an extra inclusion is harmless.
Live mode for the Explorer and Visitors#
The Explorer's raw requests list and the Visitors page get a proper Live mode. Turning it on snaps the range to the last hour (a live stream over 30 days is pointless) and restores your previous range when you stop, pins to the newest page, and surfaces brand-new pageviews as they arrive rather than waiting on the 30-second count cache (it shows "Live · showing N" without an exact total, for freshness). The Live toggle and the Refresh button fuse into one control with a countdown border that depletes over the interval and restarts on every refresh, manual or automatic.
Filter by browser version, and type a stitch value#
Two new ways to filter. Browser version is a numeric range filter on the browser's major version (Browser version ≥ 150, with ≥, ≤, or a range, like any numeric filter); pair it with a browser filter, since a version number means different things across families. And you can now type or paste a stitch value into the filter bar to scope to a single visitor, where before it was click-only; a pasted value is normalised to lowercase hex.
Beacon wire keys are now readable mnemonics#
The beacon's compact wire keys, previously an opaque numbered scheme (t1, t9, t10, and so on), are now short mnemonics: ttfb, lcp, inp, cls, and the rest. It is fully backward-compatible and needs nothing from you: the collector still accepts the old keys, so a page running an older cached bundle keeps reporting until it refreshes. The same release drops three legacy JSON blobs the beacon no longer needs (the old third-party, resource, and Long Animation Frame strings, long since replaced by structured fields), trimming a few hundred bytes off each pageview. See The RUM Beacon.
No duplicate mid-pageview update beacons#
On a page that logged both an error and a fetch/XHR call, the tracker could fire two byte-identical update beacons at the same instant: the error channel and the fetch/XHR channel each tracked their own send timer, and neither knew the other had already shipped the full snapshot. Because every beacon carries the complete cumulative state, the second one was pure duplication. The tracker now tracks unsent activity per channel and skips an update once the current state is already on the wire. Fewer requests, and no data lost: the update that does go out still carries both channels.
'Beacon' is now 'Pageview'#
The drill-down record is called a Pageview throughout the dashboard now (labels, tooltips, copy buttons, the empty and loading states, and the Markdown export) instead of "Beacon", which read as jargon. Its deep-link parameters change to ?pageview= and ?pvtab=, so old ?beacon= links no longer resolve. Opening a pageview from the raw Explorer list also writes it to the URL now, so a refresh reopens the same row and the link is shareable.
Quick +/- filters in the Explorer and pageview drawer#
Every filterable value in the Explorer table and the pageview drawer gains hover + / − buttons that filter to or exclude that value straight into the Explorer filters. In the table they float over the end of the cell, so revealing them never shifts or truncates a column; in the drawer they sit in a reserved slot beside Country, Page type, Device, OS, Browser, connection, protocol, navigation, referrer, campaign, cache status, and domain (Browser filters Chrome, not Chrome 120). The + also works on a pageview's visitor (stitch), scoping the Explorer to that one visitor.
Richer network and server-timing detail in the pageview drawer#
The pageview drawer now draws its network sections as proportional, worst-first bar lists instead of flat rows. Resources show count, size, and duration per initiator type (the sizes and durations were collected but never shown before), third parties split by domain (blocking time) and by category (request share), and Long Animation Frames rank by per-script blocking time. Server timing becomes one stacked composition bar of every backend phase (the seven promoted metrics plus your custom Server-Timing keys) with a matching legend; custom keys that already feed a promoted phase are de-duplicated, so the total is not double-counted.
Query retention now applies to fetch/XHR endpoint paths#
The tracker stripped the query from every fetch/XHR path, so a front-controller shop (/index.php?controller=Search versus ?controller=Cart) collapsed into one /index.php bucket.
- Same settings as the page URL:
collect_query_values_forkeeps the chosenkey=valuepairs,collect_query_keysthe bare key names. With neither set nothing changes. - The tracker reduces the path; the collector re-applies the same policy as a backstop.
- Configured keys are limited to letters, digits,
_, and-, so a key can never break out of the served bundle.
Filter analytics by visitor (stitch ID)#
Analytics filters now include stitch: narrow any breakdown, chart, or drill-down to one or more specific visitors by their stitch ID. The values are the same 32-character hex ids that POST /analytics/beacons returns, so what you filter by matches what you read back. Ids that aren't valid are ignored, and a filter made only of invalid ids matches nothing. It is a match filter (there is no exclude-by-stitch).
Richer beacon drill-down#
The beacon drill-down (POST /analytics/beacons) returns more per-beacon detail and can be scoped to a single error. A new error_fingerprint field returns only the pageviews carrying that error signature, which is what backs the Errors page's "open in Explorer" link. Each beacon row now also carries its error class (normal or ad-blocked), the average waterfall position of each fetch/XHR endpoint, the third-party and resource maps (counts, sizes, and durations by category), and per-script long-animation-frame attribution. The long-animation-frame data needs the v2 beacon schema and comes back empty on v1.
Web Vitals measurement aligned with web-vitals v5#
The tracker measures the Core Web Vitals the way Google's web-vitals v5 library and CrUX do, so your field numbers line up with Search Console.
- INP is the CrUX p98 estimate rather than the single worst interaction. It reads lower on interaction-heavy pages: a change in method, not a regression.
- Prerendered pages no longer create phantom pageviews or inflated timings; the tracker arms itself at activation and rebases TTFB, FCP, and LCP to that moment.
- Background tabs: paints from after the tab was first hidden are dropped and the pageview is flagged. CLS still counts shifts during prerender, matching CrUX.
Element attribution for LCP, INP, and CLS#
Each pageview records why a Web Vital was slow and which element caused it.
- Phases: LCP splits into time to first byte, resource load delay, load duration, and render delay; INP into input delay, processing, and presentation.
- Elements: LCP, CLS, and INP carry a CSS-selector path to the responsible element (plus the image URL for LCP). Dynamic ids like
#order-98123are never the anchor; the path falls back to a stable parent. - Browser support: LCP phases need Chrome 77, Firefox 122, or Safari 26.2; INP phases Chrome 96, Firefox 144, or Safari 26.2; CLS attribution is Chromium-only.
Web Vitals attribution in the dashboard#
The Core Web Vitals detail pages surface the new per-pageview attribution.
- Phase Breakdown cards on LCP and INP: the metric split into its phases as a cascade, and the same phases stacked over time.
- Phases column in the breakdown tables (slowest pages, by page, country, device); expanding a row adds a phases view scoped to it.
- Element Targets panel on LCP, CLS, and INP ranks the selectors most often responsible, led by a main-culprit callout; on LCP it can rank the image URL instead.
- The Explorer gets the phase metrics and the INP interaction-type dimension; the Visitors journey and beacon drawer show each pageview's phases, element, and interaction type.
Web Vitals attribution in the API#
The new attribution is available over the API. POST /analytics/visitors/detail returns the phase split, element selectors, and interaction type for each pageview, and the seven LCP and INP subpart metrics are queryable in POST /analytics/query, with inp_interaction_type as a groupable dimension. A new POST /analytics/element-targets ranks the elements most responsible for LCP, CLS, or INP (by selector path, or by resource URL for LCP) with each one's share and average value, site-wide or per URL; it is a dedicated endpoint because the selector columns are too high-cardinality to group on directly. As in the dashboard, these fields stay empty until the visitor's browser reports them.
Visitors opens on real journeys, and filters to checkout#
The Visitors list now defaults to sessions of at least three pages instead of every single-page visit, so it opens on journeys worth reading; the minimum is adjustable and kept in the URL. A new behaviour filter, Includes page type, keeps only visitors whose session contains a pageview of a given type, with a one-click Checkout option for the common "sessions that reached checkout" case.
Performance by cache status#
Network → Cache gains a Performance by cache status grid: the headline metrics (LCP, TTFB, FCP, Page Load, p75) with one row per CDN and origin cache status, so you can see at a glance whether a MISS is really slower than a HIT and by how much. Each row is coloured by its Web Vitals threshold and shows its share of pageviews, over the same document-navigation scope as the distribution above. The switchable Breakdown card gains a panel-level share button, and the cache chart cards get the hover share button and deep-link anchor that the Analytics panels already have.
Explorer split into Charts and Table#
The Explorer is now two pages, Charts and Table, that share one time range, filter bar, and set of saved queries, so switching between them never loses your query. Charts is a multi-chart workspace: add up to eight charts, each with its own metrics, grouping, and previous-period or percentile-band overlays, size them half or full width, and rearrange them by drag and drop. Presets (Core Web Vitals, Loading, Traffic, Errors, Cache, Backend) swap the stack for a curated set, and a saved query now stores the whole layout. Table gains a hits-over-time line above the rows, so you can see where the traffic behind them happened.
Compare two filter sets on one chart#
The Charts page can overlay a second, independent filter set on every chart over the same period, so you can compare Chrome against Safari, or one country against another. Show case B as a dashed twin series on the same axes (Overlay) or as two charts per card with a shared scale (Side by side). While a comparison is active, the KPI grid at the top switches to the B-vs-A delta.
Page type in the visitor journey#
The Visitors journey now shows the detected page type as a chip on each page-view (product, category, cart, checkout, and so on), next to the navigation-type and cache-status chips, wherever a site sends or detects one. Page-views with no page type show none.
Page type across the dashboard#
Once a shop ruleset is on (pagetype_ruleset), page type becomes a first-class dimension across the dashboard. You set the ruleset per site or cross-domain tracker under Collection, then break down and filter by page type, add it as a column in the beacons list, chart the traffic mix and Web Vitals per type with the new Page types preset, and open the classified view under Analytics → Pages. Traffic with no detected type shows as no page type.
Automatic page type for shop systems#
For shops on Shopware 5/6, OXID, Magento, or WooCommerce, fastmon can now classify each pageview into a page type (product, category, cart, checkout, and so on) with no code on your side. Turn on a ruleset per site (pagetype_ruleset) and the tracker reads just the shop framework's body controller class to infer the type. It's off by default (no DOM read at all), and a Server-Timing: pageType header, if you send one, still takes priority.
Route Load for soft navigations, and Web Vitals to come#
SPA route changes (soft navigations) used to record no loading metrics, so LCP and FCP for single-page apps came only from cold page loads and read worse than the real experience. The tracker now measures soft-navigation loading two ways: Route Load (soft_nav_load), a same-site network proxy on every browser, and real LCP/FCP on Chromium once its Soft Navigations API ships. Route Load lands in the dashboard as its own metric, kept separate from LCP/FCP and rolling out gradually. When native LCP/FCP arrive, SPA LCP/FCP p75 will start reflecting in-app navigations, a methodology change rather than a regression.
Leaner beacon wire format#
The beacon's wire keys got shorter to shave bytes off every request: lifecycle is now lcy, the campaign fields (utm_source and friends, click_id_param) collapse to two-letter keys, and empty campaign fields are dropped entirely instead of riding along as empty strings (around 110 to 130 bytes per beacon). The values are unchanged, and old cached bundles keep working: the collector accepts both the old and new keys permanently.
Backend performance page for Server-Timing#
A new Performance → Backend page turns your Server-Timing response headers into analytics: a request-path card (Edge, Origin, Backend) showing each cache status and how much traffic reaches each phase, backend sub-phases (DB, cache, external, render) as share-of-backend bars, a phase trend over time, and a matrix breaking phases down by page, page type, or cache status. Every non-promoted Server-Timing key also gets its own ranked trend. See Server-Timing.
Metrics view in the breakdown grid#
The breakdown grid (on metric detail pages and expanded Explorer rows) gets a Facts / Metrics switch. Metrics replaces the dimension panels with a compact panel per Web Vital (LCP, INP, CLS, FCP, TTFB, Page Load, TBT) scoped to the same row and filters, so you can tell at a glance whether a page is bad only at LCP or slow across the board. Each panel is the standard percentile trend or distribution, and the active view persists in the URL for shareable links.
Minute-resolution charts now cover up to 6 hours#
By minute is now a date-picker granularity for windows up to 6 hours (it used to cap at 2). Short custom ranges and relative spans like 3h pick minute buckets automatically; longer spans still fall back to hourly or daily.
Visitor journey, redesigned around key=value chips#
Each fact on a page-view in the Visitors journey is now a key=value chip: navigation type (soft-nav, reload, back/forward, prerender), CDN and origin cache status (HIT / MISS, new), error count, and the Web Vitals (FCP, LCP, CLS, INP, TTFB, load), each tinted by its threshold. Metrics sit on the left, context facts on the right, under a plain URL line with time on page. Chips with no value are hidden.
Visitors filters stay in the URL#
The behavior filters on Visitors (minimum errors, pages, duration, average LCP) are now kept in the URL, so reloading or sharing the page keeps the same filtered list.
Shared stitch visitors open correctly#
Sharing or reloading an expanded stitch visitor could land on "no page visits found": the URL only recorded the session identity, so a stitch id was looked up in the wrong column. The expanded row now pins its identity into the URL, so reloads and shared links reopen the right visitor. Behavior filters (?minErrors=…) survive the round-trip too.
The beacon sends fewer requests, and you can fine-tune it#
The tracker now keeps request volume low out of the box: on a fast load the first two beacons coalesce into one, early mid-page-view updates wait for the final beacon, and a polling page's update beacons are capped. Pages that need to change any of it can set a few window globals: __FASTMON_SAMPLE_RATE, __FASTMON_INIT_COALESCE_MS, __FASTMON_UPDATE_MIN_AGE_MS, and __FASTMON_MAX_VERSION. The defaults suit most sites. See The RUM Beacon.
Collection modes are now presets and per-setting configuration#
A site's data collection is no longer one of three fixed modes. You pick a preset (Minimal, Standard, or Full) when you create a site, then tune each setting on its own: the on-device session id and its consent timing, the edge-derived visitor signal, query-parameter names, error messages and stack-frame detail, and fetch/XHR capture. New sites start on Standard, which turns on unique-visitor counts and diagnostics while still writing nothing to the visitor's device. The old collection_mode values keep working as a deprecated API alias. See Tracker settings.
Visitors Explorer, with stitch as a first-class identity#
The Sessions page is now Visitors, at /visitors (old links redirect, shared links keep working). Stitch and session are treated as equals: the identity toggle defaults to the site's preferred identity, and a stitch row expands into that visitor's full page-view journey, the same drill-down sessions already had. The stray "visitors on this page" count card is gone.
Cross-domain trackers, and one-click site provisioning#
A cross-domain tracker (one embed across several domains) now carries the same data scope as a site, applied to every domain it runs on; only the visitor signal stays per domain. Domains it observes that aren't sites yet appear as detected domains, and Add as site creates the site in one click, inheriting the tracker's data scope, collector config, and bundle version. See Applications.
One bad value no longer drops a batch of beacons#
A single beacon carrying a non-finite or out-of-range connection or timing value used to fail the whole batch insert, dropping every other beacon in that flush. Such values are now coerced to "unknown" per field, so one malformed or hostile beacon can't take down the legitimate traffic around it.
Breakdown lists: +/- filter actions and share of total#
The breakdown lists on Analytics (Sources, Pages, Countries, Devices, and the Audience and Cache top lists) gain the hover actions from the metric detail pages: + filters to a value, − excludes it. Where a dimension can't be excluded, the − is disabled with a tooltip. Each row also shows its share of the total next to the count (for example 12% · 4.3K), computed across the whole window, not just the visible rows.
Org-scoped navigation: your site selection follows you#
Every monitoring page now lives under an org-scoped URL, and the site is a filter that follows you from page to page like the time range. With no site selected you see the whole org aggregated, and the sidebar switcher has an explicit All sites state. The switcher is now multi-select: click a site to jump to it, or use its checkbox (or Cmd/Ctrl-click) to combine several. Site tags now actually scope the data, with multiple tags combining as AND; the old tag selector that had no effect is gone. Old per-site links keep working through a permanent redirect that carries the site over as the filter. Typing org: in the Cmd+K palette narrows the search to organizations.
Colorblind-friendly mode#
A new Colorblind-friendly mode under Account, Appearance swaps the status and chart palette to a red/green-safe scheme (blue, orange, vermillion), so the Good / Needs-improvement / Poor ratings read as blue, orange, and red across the dashboard, its charts, the geo map, and the heatmaps. Status indicators also carry icons, so meaning never depends on colour alone. The setting is remembered on your device.
AI Ask no longer repeats its answer#
The AI Ask assistant sometimes streamed the same answer twice and stored two copies, so the reply showed up doubled and stayed doubled after a reload. An output check was regenerating the answer after it had already been sent; that self-correction is gone, and the check now only logs issues instead of triggering a resend. Each answer is sent and stored once.
Cache monitoring, reorganised around the request path#
The Cache page is rebuilt around the three layers a request passes through, with a Browser / CDN / Origin switcher.
- Same panels per layer: hit rate, status distribution, cache status over time (absolute/percent toggle, click-to-isolate legend), and page attribution.
- Browser cache is framed around returning views (reloads, back/forward), without a Good/Poor rating; a "First navigations" figure gives context.
- CDN and Origin read native hit-rate metrics and exclude soft navigations, which carry no cache status.
- Resources by type: script, CSS, images, fonts, and other, with a count/bytes toggle. The dashboard gets one combined cache widget with the same switcher.
Release markers on every chart, with a clearer hover#
Release markers now work the same on every chart. The Analytics page charts (visitors, pageviews, views per visit) show them for the first time, and every time-series chart draws them from one shared implementation, so app and tracker markers look and behave identically. Charts that used to hide a release falling inside your selected window but outside the loaded data (LCP, cache hit rate, synthetic, Explorer) now show every release in range. The hover is reworked too: a band along the top of the chart makes markers easy to grab, close-together markers group into one target with a count, and the marker tooltip no longer collides with the chart's data tooltip or clips at the edge.
June 2026
Shareable metric-panel view#
The Over time / Distribution / Both toggle on the metric detail pages (LCP, FCP, CLS, INP, TTFB, Page Weight) is now reflected in the URL, and the panel has a share-link button. A copied link reopens the exact view with the time range pinned. Shared links to a panel view or a breakdown sub-panel keep their view (the view, pview, and panel params now survive navigation) and scroll all the way to the target panel.
Attribution tables show every row, with per-column aggregation#
The slowest/worst pages, the breakdown grids, and the Cache and Errors attribution tables no longer hide low-volume rows behind a ≥1%-of-pageviews (min. 30) floor. Every row is shown; narrow by volume yourself in the Explorer (for example beacons:ge:N). A count-only metric (Requests / Visitors) next to a value metric no longer forces the whole table to Count: each column uses its own aggregation, so LCP shows its p75 rather than a meaningless count. The Explorer KPI tiles behave the same way.
Explorer: distribution of a dimension over time#
In the Explorer you can now group by a categorical dimension, such as CDN or origin cache status, and chart how each of its values (HIT, MISS, EXPIRED) develops over time with a Line, Stacked Line, or Stacked Bar view, rather than only the aggregate breakdown. Stacked views toggle between 100% (each value's share per interval) and absolute counts. You can overlay a second metric, such as TTFB, as a dashed line on a right-hand axis for context. Choosing a Group-by dimension no longer forces the chart to Bar: Line, Stacked, and Pie are kept where they make sense, and the chart-type picker greys out options that can't render the current query.
Percentage metrics in the Explorer#
Ratio metrics such as cache hit rate now render as a real percentage (for example 80%) in the Explorer charts and tables, instead of showing “0.8%”.
Good / Needs-improvement / Poor split on the distribution cards#
The dashboard's distribution metric cards now show the Good / Needs-improvement / Poor split as colour-coded percentages in the card header, for every metric with thresholds (the Core Web Vitals plus FCP, TTFB, and Page Load), while the rating badge and trend arrow stay in place. The card histogram is larger, and its bars are now both hoverable for detail and clickable to open the metric detail page.
Filter, exclude, and deep-link breakdown panels#
The “What's driving it” breakdown rows now carry hover + / − actions to filter to or exclude a value in place, matching the per-row grid panels. For dimensions that can't be excluded, such as page URLs, exclude is shown disabled with a reason. Each breakdown panel also has a share button that deep-links straight to that panel; opening the link scrolls to it and briefly highlights it.
Cleaner distribution presentation#
A cleanup of the detail pages.
- The value distribution lives only in the top "Over time" panel and is no longer repeated at the bottom.
- Breakdown panels show each value's share of pageviews next to the count, and share links restore the chosen chart view of an expanded row.
- CDN and origin cache breakdowns get an "unknown" bucket, shown only when at least 1% of views have a known cache status.
- The "Open in Explorer" button on the Errors page is gone; drill down from the tables instead.
Ad-blocked tracker count is now returned by the API#
Tracker calls dropped as ad-blocked were already excluded from your error rate, but not reported anywhere. The read APIs now return that count: the fetch/XHR endpoint breakdown and the per-beacon detail include a blocked count alongside aborts, and the error-types overview returns a new blocked_count. Your error rate is unchanged; this only surfaces how many calls were excluded, so the dashboard can show “N excluded as ad-blocked” rather than silently dropping them. See the API reference.
Metric detail pages redesigned around one “Over time” panel#
Each metric detail page (LCP, INP, CLS, TBT, Page Weight, and the load-timing pages) now leads with a single “Over time” panel that you switch between the percentile trend, the value distribution, or both side by side. The distribution buckets and the Good/Poor zones are clickable: they open the Explorer scoped to that value range, carrying over your current time range. Short windows (2 hours or less) now chart by the minute instead of by the hour, so a one-hour view is no longer nearly empty.
“What’s driving it” breakdown on the detail pages#
Every metric detail page now has a breakdown table you can switch across dimensions (Page, Country, Device, Browser with or without version, Connection, and CDN/Origin cache), with a TTFB column for context and a Most viewed / Worst / Best toggle. Expanding a row reveals the same over-time panel scoped to that value, plus a grid of the other dimensions where you can filter to or exclude a value in place, or open it in the Explorer. This expandable view is the same on the metric detail pages and in the Explorer: a grouped Explorer result row opens the exact same panel and grid.
Desktop / Mobile quick filter#
An All / Desktop / Mobile quick filter now sits next to the time picker on the metric detail pages, the Dashboard, and Analytics, applying the same device filter as the filter bar. The toolbar (percentile · time range · device · filters · refresh) is now identical across these views.
Clearer Explorer filters, breadcrumb drill-down, and pinned share links#
A round of Explorer work.
- Filter rows labelled Query filters (server) and Result filters (browser), on one line.
- Status color is always on for value metrics; count metrics expand to the breakdown grid and a trend.
- Breadcrumb drill-down: "Core Web Vitals › LCP › Explorer", with a back arrow to the exact spot, expanded row included.
- Share links pin the time range to absolute timestamps.
- The standalone Pages view is retired in favour of Analytics.
Large data volumes and mobile layout#
Transfer-size and page-weight values now scale up to GB and TB instead of capping at MB (a ~1 TB total no longer reads as “1069213.0 MB”), and large third-party call counts show in compact form (for example “1.1M”) with the full number on hover. On small screens, the expanded breakdown grid no longer overflows off-screen, its row actions are always visible on touch, and the time picker no longer runs off the left edge.
Error rate no longer counts ad-blocked trackers or page-teardown noise#
Third-party trackers blocked by an ad-blocker or tracking protection (Bing UET, Meta Pixel, Google Tag Manager, and similar) no longer count as errors, and neither do requests the browser cancels when a visitor leaves the page. They are tagged and kept, not dropped, so your error rate reflects real, fixable problems. One real dashboard fell from a ~60% error rate that was driven almost entirely by a blocked bat.bing.com.
Tracker stops reporting page-teardown failures as errors#
When a visitor navigates away or closes the tab, the browser cancels in-flight requests and can interrupt running scripts. The tracker now detects teardown (via pagehide and visibilitychange) and counts those network failures as aborts rather than errors, and drops the JavaScript exceptions thrown during teardown, so neither inflates your error count.
Passwordless login with passkeys#
Sign in with a device passkey in one step, with no password and no separate 2FA code. Manage passkeys under Account → Security (add, name, rename, remove). Sensitive actions (regenerating your API key, toggling 2FA, changing your email, connecting an app, deleting your account) now ask for fresh re-authentication ("step-up") with your strongest enrolled factor, a passkey or TOTP.
Cache-hit visibility#
New CDN Cache and Origin Cache (HIT/MISS) columns and a cache-status filter in the beacon explorer.
AI “Ask” error drill-down#
The AI Ask error drill-down returns its summary and time-series correctly instead of erroring on every call.
Cross-domain trackers#
Run a single tracker across several domains of the same codebase (for example shop.de and shop.fr); beacons are routed to the correct per-domain site automatically, so per-site analytics and access control are preserved. Create, edit, rotate hashes, and delete them on the organization's Sites page, with a "detected domains" list that suggests unregistered hosts to add as sites.
Partner self-service#
Organizations flagged as partners can provision and manage client organizations: create a client (optionally emailing an owner invite), list managed clients, view aggregated beacon usage, and detach a client. Organization → Team also lists partners that can manage your org and lets owners revoke that access.
Collector modes#
The binary fastmon / first-party collector toggle is now three modes, each shown with a first-party / third-party badge: Default (fastmon's collector), Custom (a URL you control), and Relative (a same-origin /c/<hash> endpoint). (API: a new collector_mode field on sites and trackers.)
Deleting a site no longer fails on child rows#
DELETE /sites/{id} no longer errors when the site has releases, weekly summaries, or feedback.
IDN and trailing-dot domains no longer lose traffic#
Internationalized hostnames (e.g. münchen.de) and trailing-dot FQDNs were rejected or dropped at ingest; hostnames are now canonicalized consistently at registration and ingest.
Per-domain breakdown on multi-domain setups#
Beacons now record the host the tracker actually reported instead of the configured site domain, so the per-domain breakdown is correct again. Historical rows keep their old value.
Resource-load errors group separately#
In anonymous mode, distinct broken first-party resources on the same host no longer collapse into a single error (applies to new beacons).
More beacon fields and columns#
New browser_version field on the visitors list, and five new projectable columns on /analytics/beacons (version, tracker_version, site_id, lifecycle, delivery_method). See the API reference.
More accurate web-vitals percentiles#
Median and percentile values for web vitals in long-range analytics are computed more accurately, correcting a systematic underestimate of the median.
Rebuilt time-range picker#
The picker now leads with an absolute range that also accepts relative input (now-6h, or shorthand like 4h / 30d); relative ranges stay live against "now", with a "Now" pin and an r keyboard shortcut to refresh. Metric thresholds can now be typed in natural units, e.g. lcp >= 2.5s or total_byte_weight < 512kb.
Visitors-list filter crash#
Filtering the visitors list by device type, browser, country, or OS no longer errors.
“Sessions” is now “Visitors”, along an identity axis#
The sessions metric is replaced by visitors everywhere: charts, the metric picker, and alert rules. A visitor is counted by identity: stitch (edge-derived, consent-free, the default) or session (consent-gated). The explorer gained a Stitch/Session switch with a per-row identity badge, and each site has a new default-visitor-identity setting. See Visitors and sessions.
Connected apps (OAuth)#
fastmon can authorize first-party app integrations: an in-app consent screen, and a "Connected apps" tab in account settings where you can review and disconnect authorized apps.
Brand lowercased to “fastmon”#
The brand is now written lowercase across the app, authentication, emails, and SEO metadata.
Invited users land in their organization#
Accepting an invite no longer drops you into a second empty organization, including Google/GitHub sign-up.
Full stack-frame paths kept for de-minification#
The tracker keeps the complete minified stack-frame path so JavaScript errors de-minify to the correct source location.
Fetch/XHR calls flushed mid-pageview#
The tracker now reports every fetch/XHR call during a pageview, not just slow outliers, so the Fetch/XHR view is complete.
Value distributions and shareable deep links#
Core Web Vitals and performance/network detail pages show the metric's distribution with Good / Needs-improvement / Poor zones and p50/p75/p90 markers; each page row expands to its own distribution. Share buttons reopen the exact page or row, and the per-page error breakdown plus multi-value and exclude (≠) filters arrived alongside.
Soft navigations marked as nav_type=softnav#
The tracker marks client-side route changes as nav_type=softnav, so single-page-app navigations are distinguished from full page loads in analytics.
Fetch/XHR (AJAX) monitoring#
A dedicated page under Network analyzes your app's fetch/XHR calls: p75/p95 tail latency, a blocking-call filter, aborts counted apart from errors, response size, a per-call backend-phase mini-waterfall, and a first-party / third-party scope toggle. Capture is controlled per site by a "Store subrequests" toggle. See Analytics.
Device-type breakdown, auto theme, richer beacon detail#
A device-type tab (desktop / mobile / tablet), an "Auto" theme that follows your OS preference, and a far richer per-beacon detail panel (server-timing phases, cache status, called endpoints, attribution).
Command palette (⌘K)#
One searchable menu drives the whole app: jump to any page, switch organization or site, change the time range and granularity, and add or clear filters. The filter control also moved to the header, next to the time picker, with an active-filter count badge.
Per-site tag filter in the sidebar#
The sidebar tag filter only narrowed the site list and never affected analytics, so it was removed; tags are planned to return as a real cross-site analytics scope. See Sites.
May 2026
API tokens are now “API keys”#
The personal access token is now called an API key consistently across the API, the SDK examples, and account settings. Same credential and behavior; only the name changed, to match the backend. See Authentication.
URL drill-down dropped rows on full-collection sites#
On sites in full collection mode, the per-URL drill-down silently dropped some rows; it now returns the complete breakdown.
Minute-level granularity#
Analytics queries over short windows can now bucket by the minute (in addition to hour/day), so very recent traffic and incidents show up at the right resolution.
Raw IP, User-Agent, and referrer no longer reach the backend#
A privacy-focused edge cutover: the edge derives everything fastmon needs (country, device, referrer source) and the backend never receives the raw IP address, User-Agent string, or full referrer URL. Less personal data is stored, by design. See Privacy.
Stitch: a consent-free cross-pageview identifier#
A new edge-derived, bounded-lifetime identifier (stitch) links pageviews from the same visitor without cookies or consent. It later became the default visitor identity. See Visitors and sessions.
Campaign attribution (UTM + click IDs)#
Traffic now carries utm_* parameters and ad-platform click IDs, so you can break analytics down by campaign source, medium, and campaign (Plausible-aligned).
Referrer reclassified into source and medium#
Instead of a raw referrer URL, referrers are bucketed into a normalized source and medium (Snowplow-style), so the referrer breakdown is clean and groupable.
Five low-value fields retired from the beacon#
The tracker no longer collects device_memory, hw_concurrency, save_data, prefers_dark, or prefers_reduced_motion: a data-minimization pass that shrinks the payload and the data stored about each visitor.
Tracker slimmed down#
The tracker no longer reads document.referrer, matchMedia, or User-Agent buckets (the edge derives those now), making the embedded script smaller and the data it sends leaner.