fastmon Docs

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.

October 2026

NewDashboard

Observations, in beta#

fastmon now writes down changes it finds on its own, each with a start and an end. Observations were an admin preview and are open to every role of an organization with the Watchdog feature.

  • Shifts compare a quarter hour of traffic, TTFB, LCP, JavaScript errors, or bots with the same quarter hour on the 7 days before. A degradation shows after 30 minutes, everything else after 2 hours.
  • Trends compare the last 7 days of LCP, INP, CLS, TTFB, or error share with the 14 days before, once a day. An open trend says since when the metric sits on its new level.
  • Acknowledge marks an observation as seen, with a note for the team. It leaves the open cards and the sidebar count and still ends on its own. Owners and members can acknowledge.
  • The weekly mail lists the open trends of an application after the table, and the trends that got better as good news. An open trend replaces the week-over-week finding of its metric.

Docs: Observations.

NewDashboard

Acknowledge an error#

An error on the JavaScript errors page can be acknowledged, with a note for the team. The list shows open errors by default and the acknowledged ones behind a switch. The acknowledgement holds for the fault, so a deploy that moves the bundle keeps it.

The error rate and every total keep counting the error, and the weekly overview opens no new finding for it. Owners and members can acknowledge, and a quiet error can be reopened from its page.

NewAPI

Acknowledgements in the API#

Observations and error signatures can be acknowledged through the API. Both need the new permission observation:write, which owners and members have.

  • PATCH /observations/{observation_id} with status: acknowledged marks an observation as seen, status: open takes it back, and note holds up to 1,000 characters. Both observation lists and the sidebar count skip an acknowledged observation under status=open. It lists under status: acknowledged until it ends.
  • POST /organizations/{org_id}/error-acknowledgements with the fingerprint of an error row acknowledges the fault, with an optional note. GET on the same path lists them, PATCH /error-acknowledgements/{acknowledgement_id} changes the note, and DELETE reopens the signature.
  • The error list carries status on every row of POST /analytics/error-types, open or acknowledged, and takes it as a filter. Without the filter it returns both.
  • Audit entries observation.updated, error.acknowledged, and error.reopened record every change.
  • Observations carry reference_unit, one of share, page_views, milliseconds, or days. A trend carries evidence.since.
ChangedPlatform

Bot detection and visitor stitching#

  • navigator.webdriver counts as bot, also when the visitor was labelled hyperactive or unnamed. The bot filter now hides it. Rows written before keep their label.
  • ua_anomaly has two new values. hyperactive marks a visitor with more than 1,000 beacon requests in a rolling hour, for 24 hours. unnamed marks a desktop client whose User-Agent parsed to no browser and matched no named bot. Both count as suspect and leave the humans scope.
  • Visitor stitching rotates at 02:00 UTC instead of 24 hours after the edge started, so a visitor is no longer cut in two during the day.

Docs: Privacy → The stitch identifier.

ChangedDashboard

Filters in a pop-up, Explorer on the server, keyboard shortcuts#

  • Filters are edited in a pop-up. "Filter" in the header, a filter chip, or the f key opens it: the filters in force on the left, the values of the open filter on the right by name and by pageviews, and a catalog of every field. Nothing changes until "Apply".
  • Percentile, comparison, device, and traffic stay in the header as small menus and apply at once. The Explorer's saved queries moved into the pop-up.
  • Browser and browser version are one filter. The version range sits under the browsers you pick.
  • Explorer filters and sorting run on the server. A column's funnel filters every pageview, not only the 50 on screen. Columns are picked in a pop-up and dragged into order.
  • A pageview opens as a pop-up, and Left and Right step through the list.
  • Shortcuts: v maximizes the chart or table under the mouse, ? lists every shortcut, l starts and stops live mode. r refreshes, and the icon turns until the data is back.

Docs: Analytics.

ChangedDashboard

Pickers that search, and a new account page#

  • The application picker searches. Type an application, a domain, or a tag. Domains are grouped under their application, development applications sit in their own group, and a dot shows whether a domain sent data in the last 24 hours.
  • Several domains or tags switch at once. The circle at the end of a row collects it, and "Apply" switches to all of them.
  • The organization picker works the same way, with the organizations you opened last on top and your role and plan under each name.
  • The account page is laid out like the application settings. Profile, Appearance, Security, Notifications, and Access each show what is set, and your organizations are listed under Profile. Old links to the tabs still open the right section.
  • The avatar menu opens your account, notifications, and API keys directly and sets theme and language with labelled toggles.
ChangedPlatform

Weekly overview with fewer false alarms#

  • A new error is an alarm only once it reached the visitors. Below 0.5 % of the week's pageviews it reads "needs improvement", not "poor".
  • Severity picks the three findings of an application. Three new errors no longer push an LCP or TTFB regression out of the mail.
  • A regression that prints the same number in both weeks is left out, such as 1,180 to 1,245 ms, which both read 1.2 s.
  • "A page loads faster" opens that page, with the week before beside it.
  • A metric that stays poor has its own title, such as "Error rate still high", and the middle rating reads "needs improvement" as on the dashboard.
  • The page is laid out like the metric pages: two tiles count the findings, the mail's opening paragraph follows, and "Over several weeks" lists the open trends, which the mail already had.
ChangedDashboard

JavaScript errors grouped by fault#

  • A fault that moved to another line of the same file stays one row. The list groups on type, message shape, and file, so a deploy that shifts a bundle no longer turns a known fault into a new one. "New" and "Rising" compare on the same grouping.
  • A script on the host of one of your sites is first party on every page of the application. A shop's bundle on a translation proxy or a preview domain is no longer third party. A row whose occurrences disagree reads "Mixed".
  • Rates on sampled applications are right. The share of affected pageviews, the top 5, and the pageviews column use weighted pageviews, so the chart no longer reads past 100 %.
  • /analytics/query grouped by errors.fingerprint answers error_beacons and error_rate.
ChangedDashboard

The dashboard answers three questions#

The dashboard is rebuilt from top to bottom. A status band says whether visitors arrive, tracking runs, and observations are open. Visitor tiles and an experience panel show what changed, and every tile opens its page.

  • The experience panel has the score on top and eight metric tiles below it, each with value, rating, change, and the share of good and poor pageviews. A tile picks the chart underneath.
  • The response breakdown runs over all pages, from DNS through the Server-Timing phases to the LCP, per page type or page. Its other view ranks page types or pages by LCP, INP, TTFB, and error rate.
  • "Where the time goes" splits the LCP into its four phases and shows the first byte with and without a cache hit.
  • The globe colours countries by LCP, TTFB, or traffic and maximizes like a chart. A maximized chart is in the address as max=, so a link opens it.
  • Fewer queries: the cards of a breakdown grid load once they scroll into view.
NewDashboard

Pages#

Pages is a new entry in the sidebar under Investigate. It lists your page types first, then every page by URL with a search, each with LCP, INP, CLS, and its pageviews, ranked by pageviews or by the slowest value.

Each page and page type opens a page of its own, built like the LCP and TTFB pages. The four metrics sit on top, and a click on one opens its metric page below, narrowed to this page or page type. A page type ends with the list of its pages, and one button opens the page in the Explorer.

ChangedPlatform

Ask has a daily budget#

Ask's budget is counted per day instead of per minute, so a second question right after the first is no longer refused. Once the day's budget is used up, Ask answers again at 00:00 UTC.

ChangedPlatform

Experience score without page load time#

The Experience Score drops page load time and rates only errors from your own code.

  • New weights: LCP 0.25, INP 0.20, CLS 0.15, FCP 0.15, error rate 0.15, and TTFB 0.10. Every score moves, and some grades change.
  • first_party_error_rate is the rated error rate, good up to 1 % and poor above 5 %. third_party_error_rate sits next to it, unrated.
  • error_rate still counts every JavaScript error, but nothing rates it any more. In POST /analytics/experience-score it stays as a deprecated entry without a score.
  • Same rate everywhere: the weekly mail, the "Error rate too high" alert preset, and the dashboard's reliability tile use the first-party rate. Existing alert rules stay unchanged.

No score falls, and sites with third-party errors rise. Docs: Experience Score.

NewDashboard

Eight more metric pages in the new layout#

INP, CLS, FCP, TBT, page load, DNS, connect, and transfer size now have pages like LCP and TTFB. Each shows the value with its trend, the pageviews by rating, a summary, the distribution, and a table of where the metric is worst.

  • INP splits the slowest interaction into input delay, processing, and presentation.
  • INP and CLS list the elements behind the value, and INP and TBT list the scripts in long animation frames.
  • Transfer size shows the weight per resource type and ranks the third-party domains.
  • Compare works on all eight pages and now lives in the filter, as a chip with an x to end it.
  • A set filter turns the filter button black, or white in dark mode.

POST /analytics/metric-summary writes the summary for these metrics, with the new sentence type level and facts.unit. Docs: Web Vitals.

ChangedDashboard

A new Analytics page#

The Analytics page is rebuilt around cards that all work the same way.

  • Cards: one menu picks the dimension, and each card pages through its rows. A card's title opens every value with a search, a second dimension, sorting, and a CSV download.
  • Pages get a wide card with the page name, its URL, and its LCP.
  • Orders show as a line or as three lanes: cart, checkout reached, and ordered. The Conversion tile shows on every time range.
  • Pageviews split into humans and bots, per bot or per bot type. The split only changes the chart.
  • Live: a line above the chart shows the visitors right now, and "Watch live" opens a ticker of the newest orders or events.
  • Visitor paths: the funnel and the buyer comparison share a card, and visit depth shows how many pages a visit reaches.
  • Charts draw each bucket at its end, and the running hour is dashed towards a projection. Releases are a thin purple line.

Status codes left the Analytics page. Filtering on "Direct / none" and other empty values stays off for now. Docs: Analytics.

NewDashboard

Five-minute, fifteen-minute, and six-hour charts in your time zone#

Charts have three new resolutions, and buckets of six hours or longer start on your clock.

  • Auto picks the smallest width with at most 300 buckets. The last 24 hours show five-minute buckets, 48 hours fifteen-minute ones, and 28 days or more show days.
  • Fine widths crop a long window to its last part: minutes to 25 hours, 5 minutes to 5 days, 15 minutes to 15 days. The tile says so.
  • Your time zone: a day starts at your midnight, a week on Monday, and a month on the first. Across a clock change a day has 23 or 25 hours.
  • Time of day shows in tooltips and axes of every chart below a day, also in Ask.
  • Conversion and order stages follow the chart's resolution up to a day.

Docs: Analytics.

NewDashboard

Past windows in the time picker#

The time picker's expression field starts with the current range, such as now-7d, so you only change the number. It also takes a window in the past: 7d-3d is the window from 7 to 3 days ago. An info icon lists every expression it understands.

NewDashboard

dataLayer settings and order revenue#

The application settings have their own dataLayer section, and the events overview counts revenue per order.

  • dataLayer: read the GA4 ecommerce events, read every other push by name, pick the keys kept as properties, and keep or drop names with * for a prefix.
  • Revenue counts each order once, at its largest value, per currency. The old sums per event stay as "Value per event".
  • API: POST /analytics/events/overview returns order_revenue and value_sums. revenue is deprecated.

A thank-you page that pushes purchase on every reload counts one order per pageview. Docs: Custom events.

NewTracker

Every dataLayer push, with your keys and masked personal data#

The dataLayer bridge has two independent switches, both off by default.

  • collection.ga4_ecommerce reads the seven GA4 ecommerce names with value, currency, and items. Applications that had collection.data_layer on move to this switch.
  • collection.data_layer now reads every name the page pushes, without properties. Google Tag Manager's gtm.* pushes never count, and one name counts at most 10 times per pageview.
  • collection.data_layer_prop_keys keeps up to 30 top-level keys of a push as properties.
  • collection.data_layer_mode with data_layer_names blocks or allows up to 50 names. The collector applies it within five minutes, and it never filters fastmon.event().
  • Masking: a property key containing user, customer, email, mail, phone, password, or token is sent as [masked].
  • E-mail addresses in event properties become [email] before the event leaves the browser.

Docs: Custom events and Privacy.

FixedTracker

Events arrive behind blockers#

Custom events no longer depend on a later beacon or on sendBeacon, which EasyPrivacy blocks.

  • Within about a second: when no beacon follows, the tracker sends the event by fetch, at most 20 times per pageview. Such a beacon has the lifecycle event_update.
  • At page hide, queued events go out once more by keepalive fetch, one copy per event.
  • Single-page apps: a route change sends every queued event under the page it belongs to.
  • Wrong browser clocks: an event is stamped on the beacon's clock and counts once, also when resent on unload.
  • Names: fastmon.event("Add To Cart") is stored as add_to_cart. A name without a letter or digit is skipped.

Docs: Custom events.

FixedTracker

Browser cache hit rate counts Safari#

The browser cache hit rate now counts Safari's cache hits, so Safari's rate jumps at the release. A cross-origin resource without Timing-Allow-Origin hides its byte counts and now stays out of the rate. A pageview with no resource of known cache state stores no rate instead of 0 %.

FixedTracker

Shopify checkout pixel works with filters#

A checkout funnel filtered by viewport, connection, or page type keeps a Shopify shop's checkout and purchase instead of reading 0 %.

  • The pixel sends the viewport class, the connection class where the browser reports one, and page type checkout. Installed pixels update by themselves.
  • Login filter: the pixel has no login state, so logged_in on a Shopify scope answers 422. The dashboard offers a button that removes the filter.
  • Pixel status looks back 31 days and can take 30 seconds to show a test order.

For the first week, last week's pixel visitors in the live summary sit in a row without a page type. Docs: Shopify checkout.

NewAPI

Bucket widths and time zones in the API#

POST /analytics/query and every series route take new widths and a time zone.

  • New widths: five_minutes, fifteen_minutes, and six_hours. Widths under an hour allow at most 1500 buckets, so minute now covers about 25 hours.
  • granularity: auto picks the smallest width with at most 300 buckets, and day from 28 days. meta.granularity_options lists every width with 4 to 1500 buckets.
  • timezone takes an IANA name such as Europe/Berlin. Buckets of six hours or more then start at that zone's boundaries, and timestamps stay UTC.
  • Weeks start on Monday, with or without timezone.
  • Past windows read finalized data. Only a window of at most 6 hours ending near now reads live beacons.
  • Ask and MCP take every width, and the error, NEL, CSP, and events tools take interval.
  • Breaking: a client that switches on granularity has to accept the new values.
NewAPI

Breakdowns by the URL query#

POST /analytics/query groups and filters by parts of the page URL.

  • url_path is the URL without its query, so /p?color=red and /p?size=m add up to /p.
  • url_key gives one row per query key, and url_key_set the set of keys a page carried.
  • url_param with url_param_key groups by the value of one key. It groups only and does not filter.
  • meta.pageviews is the window's pageviews after filters, the denominator for a share.

A key whose value the application does not store answers 422 naming collection.query_values_for.

ChangedAPI

Filters on the empty value#

A text filter set to "" now selects the rows without a value. referrer_source: "" answers the direct visits, where it used to answer all traffic. exclude takes the referrer, UTM, and click_id_param columns, and Ask and MCP use empty and not_empty instead.

ChangedAPI

sample_count replaces beacons#

POST /analytics/query names its sample size sample_count, since it counts pageviews. beacons still answers as a deprecated twin. The dashboard no longer shows the sample count where the backend sends no visitors or pageviews.

FixedPlatform

Safer sign-in and notifications#

Sign in with GitHub or Google needs a verified address and no longer links onto an unconfirmed or blocked account. Webhook, Slack, and Discord notifications go only to the public address that was checked.

NewTracker

Custom events#

The tracker sends events your shop defines itself: a name and up to 30 properties.

  • fastmon.event(name, props) queues an event. A queue stub takes calls made before the tracker has loaded.
  • A click on an element with data-fm-event sends an event of that name, and every data-fm-event-* attribute becomes a property. An empty data-fm-event="" keeps a click inside a marked container from counting.
  • GA4 ecommerce pushes on window.dataLayer become events when collection.data_layer is on for the application, off by default. The tracker takes view_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_payment_info, and purchase, and leaves out a text over 500 characters.
  • No request of its own: events ride on the next beacon of the pageview, at most 20 per beacon and 100 per pageview. An event queued after the page was hidden reopens the hidden beacon.

Docs: Custom events.

NewDashboard

Your own events on the Events page#

The Events page counts and lists your shop's own events, not only the checkout.

  • Overview shows the events over time by name, the names ranked, the revenue per event and currency from value and currency, and the most frequent values of each property.
  • Events lists them one by one with their properties. A row opens to every stored field and has a share link.
  • Correct or delete a single event from its opened row, as an owner or member. Stage events derived from checkout pages stay as they are.
  • Filters: the name picker takes any name, and URL, page type, login state, connection, and viewport class narrow the events too.

The visitor journey shows the events with their properties. Docs: Custom events.

NewAPI

Custom events in the API#

The API stores, counts, and corrects any event name, not only the checkout names.

  • POST /c/e/{collector_hash} takes a single event of any name from any browser, without the tracker.
  • POST /analytics/events/overview counts the events of a window per name, over time, as revenue per currency, and per property value.
  • PATCH and DELETE /sites/{site_id}/events/{event_id} change or remove one event. The audit log records event.updated and event.deleted.
  • Breaking: name and source on an event row are strings, no longer enums, and source gains tracker. A list cursor from before the change answers 400.
  • Ask and MCP gain get_events, and list_sites reports event_tracking and collect_data_layer.

Docs: Custom events.

ChangedPlatform

The checkout funnel counts events only#

Checkout stages and buyers now come from events on every site and window. A pageview's checkout_stage no longer counts as a stage.

  • Exact counts: the funnel, its stages, cohorts, journeys, and the live summary's purchases count exactly at any sampling rate. Visitors stay estimated, so the conversion rate divides exact buyers by estimated visitors.
  • purchase reaches the success stage beside order_placed, and a buyer with both counts once.
  • Filters: URL, page type, login state, connection, and viewport class filter the events exactly. Campaign and referrer filter on the visitor's entry pageview.
  • Breaking: the checkout routes answer 422 for technical page filters, a metric range, a checkout stage filter, and the identity session. Under session the visitors list has no checkout stage, outcome, or stage counts.

Docs: Checkout funnel.

NewDashboard

The Partnerfloor#

Partner organizations get the Partnerfloor under Partner in the sidebar: every live client as a zone on one stage. A zone's size follows the client's share of the last hour's traffic, its colour the LCP p75. It shows the people, cart and checkout, today's orders, and the change against last week.

A click on a client opens its Shopfloor in place. POST /organizations/{org_id}/partner/live serves the floor and needs partner:read and analytics:read. Docs: Analytics → Partnerfloor.

NewDashboard

Compare mode on the LCP and TTFB pages#

A "Compare" button next to the time range sets a second window. It can be the period, day, week, or month before, any window you type, or a release against the one before it.

  • The summary says what changed against that window, and the value tile shows the change.
  • Every chart draws the second window beside the first: the trend dashed, the distribution as an outline, the phases as a faded bar.
  • "Where the LCP is poor" and "Where the TTFB is slow" add the window's value and the change per row, largest regression first.

The comparison is part of the page's link. Docs: LCP → Compare.

FixedDashboard

Table columns widen again#

Dragging a column edge in the Explorer table capped every column, so the URL column could only get narrower. Columns widen again, and a double-click on an edge fits the column to its content.

ChangedAPI

Compare with any window or a release#

compare_to takes a window {from, to} of any length or a release {release_id}, beside the four mode strings.

  • Where: POST /analytics/query, /analytics/funnels/checkout/stages, /analytics/error-types, /analytics/histogram, and /analytics/metric-summary. Each echoes the window it used as request.comparison_time_range.
  • A release compares from its released_at for the length of time_range, up to the next release or now. Release rows carry window for use as time_range.
  • Breaking: facts.previous_period on the metric summary and previous_period on error-type rows are now comparison.
  • Deprecated: compare_to_release_id still works. Send a compare_to window instead.

With compare_to, the metric summary says what changed instead of how the metric stands, and it covers TTFB too. Docs: Compare releases → Compare metrics.

NewDashboard

A new TTFB page#

The TTFB page is built like the LCP page: the value with rating and trend, the pageviews by rating, and a short summary.

  • Where the time goes splits the first byte into connection, server, the way in between, and the wait nobody measured. Sites with 103 Early Hints see where the browser counted the first byte.
  • With Server-Timing the server part splits into edge, origin, database, cache, search, HTTP calls, own code, and templates.
  • Where the TTFB is slow ranks page types, pages, cache status, login state, and origin hosts by their slow pageviews.
  • Cache and origin shows each cache layer with and without a hit, and calls out an origin host unlike its peers.
  • History draws one line per page type on the TTFB and LCP pages, and the panel can fill the window.

Docs: TTFB → How to see TTFB.

NewDashboard

Where a visitor came in#

The visitors list shows the source and channel each visitor came in from, such as Google search or a newsletter. The visitor pop-up marks every return from outside on the timeline and in the journey, with the campaign. In the API, journey steps carry arrival, and journey[0].arrival is the entry source.

Reloads, route changes, and the way back from a payment page never count as a return. Docs: Sessions → Arrival.

ChangedPlatform

SPA route changes count as internal traffic#

Route changes of single-page apps and BFCache restores now have the source internal. They no longer repeat the source and medium of the landing page. On an SPA site, views by referrer source show fewer pageviews for named sources and direct, and stored history follows the same rule.

ChangedPlatform

Choose your password after confirming your address#

Registering asks for no password any more. The confirmation link opens a page where you choose it, and an expired link offers a new one. An invite still asks for the password on the form.

In the API, POST /auth/verify-email takes the token and the password, and GET /auth/verify-email is gone.

ChangedPlatform

Unknown hosts on a one-site application#

The site_policy "Suggest" now drops a beacon for an unknown host on an application with a single site too, and suggests the host as a site. Before, every host landed on the one site, such as 127.0.0.1 from a test run. A www. host or a separate checkout host needs a site of its own. Docs: Tracker → site_policy.

NewDashboard

fastmon sets the sampling rate#

Under Visitors & sampling, "Set the share automatically" lets fastmon set a production application's sampling rate from your booked pageviews. It moves at most one step a day, between 100 and 1 %. It applies only to plans with a fixed number of pageviews.

The manual rate now goes down to 1 %, and the API field is collection.sampling_mode. Docs: Applications → Sampling mode.

FixedDashboard

Time charts keep their right edge#

A time chart over 6 hours no longer drops to the floor at its right edge. The running bucket is drawn as in progress up to where the data is complete, and the rest is hatched.

ChangedPlatform

Partners act with their own role#

A partner's people act in a client with the role they hold in the partner organization: owner, member, or viewer. A direct membership in the client still wins.

  • New in a client: a partner can connect org-owned apps, such as the Shopware plugin, and issue org keys.
  • Still not allowed: deleting the client, transferring ownership, managing billing or partner links, and demoting its owners.
  • Credentials stay: keys and connections a partner created carry via_partner_org_id and keep working after a detach. Detaching lists them, so the client can rotate or disconnect them.

Docs: Organizations → Partner access.

ChangedDashboard

The dashboard opens where you left it#

Opening fastmon lands on the scope you picked last: an application, a set of sites, a tag, or the whole organization. On a first visit it opens the production application as a whole instead of one of its sites.

NewTracker

Page type from a meta tag#

The tracker reads the page type from <meta name="fm-page-type" content="product"> and sends it as pt, in every privacy mode.

  • Fallback name: page-type counts when the prefixed tag is missing or empty. The last tag of a name wins, cut to 32 characters.
  • Read on every beacon: a single-page app that rewrites the tag on a route change labels the new pageview. The tracker reads it again one second later, for routers that update the head late.
  • Soft pageviews send the tag only once it has changed since the first read. A tag that never changes stays on the landing page.
  • A BFCache restore sends the restored page's own tag.
  • Order: a Server-Timing page type wins, and the tag beats body classes, URL rules, and detection. page-type-sources lists it as meta_tag.

Outside Shopify, brand now counts as a category and cms and blog as content, instead of Other. Docs: Applications → Page-type detection.

ChangedPlatform

The plan `beta` is now `free`#

free is fastmon One without payment: every feature, unlimited pageviews and applications. The API no longer accepts or returns beta, and a request that sends it gets a 422. Synthetic Monitoring allows 5 manual and 10 automatic pages on free, one, and partner alike, down from 10 and 20 on partner. Docs: Organizations → Plans.

NewAPI

Partners cover features per client#

Every organization lists its features, and a partner decides which ones it covers for each client.

  • features on the organization lists each feature as covered by a partner, granted by fastmon, included in the plan, or locked. Nothing is locked yet.
  • PATCH /organizations/{org_id}/partner/clients/{client_org_id} sets the features and pageviews a partner covers, within its contract at GET /organizations/{org_id}/partner/contract.
  • billing on a partner link is true whenever the link covers anything.
  • The audit log has three new actions: org.grants_changed, partner.contract_changed, and partner.coverage_changed.
NewAPI

Served rates and first byte phases#

POST /analytics/query takes new metrics, and alert rules, the histogram, Ask, and MCP take them too.

  • cdn_served_rate and origin_served_rate: the share of pageviews a cache answered from its stored copy (HIT, STALE, UPDATING, or REVALIDATED). cdn_hit_rate and origin_hit_rate keep counting HIT only.
  • ttfb_phase_connect, _edge, _origin, _backend, _unattributed, and _unmeasured split each pageview's first byte. Their averages add up to the average TTFB in any grouping.
  • ttfb_phase_db, _http, _render, _search, _kv, and _code split the backend part.
  • metric_ranges takes ttfb_final and the Server-Timing phases from edge_dur to code_dur. A pageview without the measurement matches no bound.
NewAPI

Last period's count in the error list#

POST /analytics/error-types takes compare_to: previous_period and then gives every row previous_period.count. A signature outside last period's top 50 gets its real count instead of showing as new. Other modes answer 422.

FixedPlatform

Histograms and alert rules count a pageview once#

POST /analytics/histogram and alert rules counted a pageview once per stored beacon version, so an early beacon weighed twice. Both now read the same data as the charts. An alert window over 6 hours lags 15 minutes at its newest end, like the charts.

FixedPlatform

No more stale DNS and connect times#

A DNS lookup or TCP connect over 60 s, or longer than the first byte around it, is stored as empty. A woken tab had put minutes into these averages. Stored rows lose the connections longer than their first byte too, while values over 60 s from before 12 October stay.

NewAPI

An example page for every element#

POST /analytics/element-targets names one example_url per element, for every metric and grouping. With a target, it counts only that element's pageviews, and group_by: "url" lists the element's own busiest pages. group_by also takes page_type and device_type.

NewDashboard

A new LCP page#

The LCP page under Core Web Vitals is rebuilt. Two tiles show the LCP and the share of pageviews measured, and a short summary links to the sections below.

  • Where the time goes splits the LCP into server response, load delay, load duration, and render delay against the web.dev targets.
  • Where the LCP is poor ranks page type and device, or another dimension, by pageviews over 4 s. A row opens as a pop-up with its own chart.
  • The LCP elements list each element with selector, kind, discovery path, and phases. Its detail checks HTML discovery, preload, fetchpriority, lazy loading, and browser cache, and has its own link.
  • AI prompt on an element builds a prompt for an assistant with the numbers, the checks, and what fastmon does not collect.
  • A pageview in the Explorer shows its LCP element the same way and links to it on the LCP page.

Docs: LCP → How to see LCP.

ChangedDashboard

Calmer charts on every metric page#

The charts on the metric pages show less and are easier to read.

  • The trend draws the selected percentile alone against the rating zones, in the chart and in its tooltip. Its pageview bars count the traffic on a sampled application.
  • The distribution shows the zones as a scale under the bars, without estimated percentile markers.
  • The breakdown grid shows each row's phases as a stripe, and icons for pages, connections, and cache statuses.
  • Counts on the stacked charts of NEL, CSP, Fetch/XHR, and cache status are whole numbers such as 150,000 and no longer cut off.
  • On a phone, charts keep 16 px to the edge, and a trend labels every other date.
NewDashboard

The visitor's network#

fastmon stores the network each visitor comes from, as asn and isp, in every privacy mode. The edge derives it from the IP like the country.

  • NEL and CSP list the networks of the failing visitors in a Networks card. A carrier outage shows as one row, such as "Deutsche Telekom AG".
  • Network is a filter, include only. Every metric page and the Explorer break down by it.
  • Analytics shows the top networks in a tab beside the countries.
  • The pageview detail names the visitor's network in its top grid.
  • "Network type" is the new name of the card for residential and datacenter traffic.

Docs: Privacy → What we collect.

ChangedDashboard

Explorer groups open in place#

A click on a grouped row in the Explorer shows that group's pageviews as the usual list, with its columns, filters, and pager. A bar above steps to the previous or next group and back to all, and the open group is part of the link. On a phone, both Explorer tables show one card per row.

ChangedDashboard

Setup guides come with your headers#

The setup guide links on the NEL and CSP pages open with ready-to-copy headers for the selected application. A view across several applications opens the guide without them.

NewAPI

Break down by the visitor's network#

asn is a dimension and a filter of POST /analytics/query, the NEL and CSP breakdowns, the MCP breakdown tools, and the live view. A group by asn also returns isp, the network's name. Every row-level response with country, such as beacon rows, visitors, errors, and NEL and CSP reports, carries asn and isp too.

NewTracker

Three checks on the LCP element#

The tracker records three causes of a slow LCP image on every pageview, in every privacy mode.

  • lfp: the fetchpriority of the image, or of the preload that fetched it (high, low, or auto).
  • llg: the image's loading, normalized to lazy or eager.
  • ldv: whether the resource came from the browser cache or over the network. A 304 revalidation counts as network, and a soft navigation reads only its own resource entries.

The cache check comes mostly from Chromium. Firefox and Safari leave it empty for images from their in-page cache, and every browser does for a cross-origin image without Timing-Allow-Origin. POST /analytics/element-targets gives each LCP element fetch_priority_high_rate, lazy_rate, and cache_rate, and POST /analytics/query groups by lcp_fetch_priority, lcp_loading, and lcp_delivery. Docs: LCP → How to see LCP.

FixedDashboard

Ask uses half the tokens#

An Ask question costs about half as many tokens, so a question with one tool call no longer hits the organization's per-minute limit on its own. A refused question answers with its own status instead of a 500. An exhausted budget is a 429 with retry_after_seconds.

NewDashboard

NEL and CSP reports in groups#

The Reports tab of Network Error Logging and Content Security Policy groups and pages its reports.

  • Group by visitor, page, or URL: the failed request on NEL, the blocked URL on CSP. A group opens in place to its reports and sorts by most reports or newest.
  • Pages of 25, 50, or 100 reports with Previous and Next, instead of loading as you scroll.
  • An opened report is in the URL, so the address links to it, also outside the current period.
  • CSP is built like NEL: the reports as a table, and "Add directive" in the filter panel picks any directive.
  • No sampling badge on either page. The browser sends every report, and fastmon stores all of them.

Docs: Content Security Policy → The page.

NewAPI

NEL and CSP breakdowns group by visitor and URL#

POST /analytics/nel/breakdown and POST /analytics/csp/breakdown group by stitch, status_code_range, and bot_name. NEL also groups by request_url, error_type, and path. Each key goes back into its filter and selects exactly that group's reports. Every row carries first_seen_at and last_seen_at, and order_by: {"field": "last_seen_at"} puts the newest group first. Docs: Network Error Logging → API.

NewAPI

NEL and CSP in Ask and MCP#

Ask and MCP clients read Network Error Logging and Content Security Policy reports with six new tools: get_nel, get_nel_breakdown, list_nel_reports, get_csp, get_csp_breakdown, and list_csp_reports. They take the filters of the dashboard routes and count the same. A row from list_csp_reports carries no original_policy. Docs: MCP → What the model can read.

ChangedAPI

Four MCP tools answer with the dashboard's field names#

get_server_timing, get_fetch_xhr, get_resource_breakdown, and get_third_party_impact run the dashboard panel's queries and answer with its field names. A client that reads the old keys has to switch.

ToolOld keyNew key
get_server_timingcache_status as {layer: {status: count}}cache, a list of layer, cache_status, count
get_server_timingphases[].phase edgeedge_dur
get_server_timingsamplescount
get_server_timingtop_keys[].values[].valuetop_keys[].values[].desc
get_fetch_xhrerrors, status_errors, total_time_mserror_count, errors_by_status_code, impact_ms
get_resource_breakdowntotal_bytes, resources, total_count, avg_duration_mssize_bytes, data, resource_count, avg_ms
get_resource_breakdownsize_percent from 0 to 100share from 0 to 1
get_third_party_impacttotal_top_n_requests, domains, domain, request_countresource_count, data, host, count

Earlier Ask conversations keep the old keys in their stored tool results.

NewAPI

Element targets describe each LCP element#

POST /analytics/element-targets returns a distribution per target with p50, p75, p90, and p95, so a client switches the percentile without a second request.

  • With metric: "lcp" each target carries the means of the four LCP subparts, the most frequent lcp_discovered_via, and image_rate.
  • With by: "element" it adds the resource_url serving most of the target's pageviews and resource_url_count.
  • metric in the response is the request's enum, with the same values lcp, cls, and inp.

group_by: "url" no longer fails with a 500 on shops with many pages. Docs: LCP → How to see LCP.

NewDashboard

A visitor opens as a pop-up everywhere#

A visitor link opens the visitor's journey over the page you are on, and closing it brings you back. This works from NEL and CSP reports, the Explorer table, the pageview detail, and checkout events.

  • Cmd or Ctrl click still opens the Visitors page in a new tab.
  • A visitor without pageviews in the period shows their reports and events in order, with a note.
  • Live mode has its own switch in the pop-up and keeps the journey on screen while new pages arrive. It refreshes every 7 seconds on every page that has it.

Docs: Analytics → Visitors.

FixedPlatform

Visitor counts across several sites#

A multi-site or organization-wide scope counted stitch visitors, whatever the applications stored. An organization whose applications keep only session ids read 0 visitors there and now reads its real count.

  • Applications that disagree count by the identifier most of them store. Each application counts once.
  • No identifier stored: visitors is null instead of 0.
  • meta.is_identity_mixed is true when the count is a lower bound. POST /analytics/query names the identity it counted by in request.identity.

Docs: Visitors and sessions.

NewPlatform

E-invoices over Peppol#

Business customers in Germany, the Netherlands, and Belgium with a VAT id also receive their invoice as an e-invoice over Peppol. The VAT id is the Peppol identifier, so it has to match your registration. The PDF and the invoice mail stay as they are. Docs: Organizations → Plans.

ChangedAPI

Up to 20 metrics per query#

POST /analytics/query takes up to 20 metrics instead of 10. The Server-Timing table per page type no longer fails with a 422 on sites that report every phase.

NewDashboard

Show one segment of the chart#

Cmd or Ctrl click on a segment in the Analytics chart legend shows only that segment, such as one bot of the bot split. The same click again brings all segments back. A plain click still hides a segment, but never the last one. Docs: Analytics → The dashboard.

ChangedDashboard

Shopfloor orders count to the second#

Today's orders on the Shopfloor count up to the request and refresh every 10 seconds. The figure says up to when it counts ("as of 14:32"), and last week runs to the same clock time. A sale no longer drops back out of the header while the server catches up. On the API, today.time_range.to of POST /analytics/live-summary carries that instant. Docs: Analytics → Shopfloor.

ChangedAPI

Error counts and Ask answers match the dashboard#

A few readers now count the way the dashboard does.

  • error_count of the error list counts every JavaScript error in scope, not only the listed signatures. Each row's share divides by that total.
  • An error drill-down whose occurrences sum to 0 shows its first and last seen time and its samples.
  • Ask and the MCP tools get_overview, detect_changes, and get_cwv_distribution read a p75 with the dashboard's method, so their numbers can move slightly.
  • get_cwv_distribution rates with the dashboard's cutoffs. A CLS of exactly 0.1 is good, and a reported 0 counts as good.
ChangedPlatform

URL patterns with segments and alternatives#

Page type URL rules and checkout paths take a subset of the URLPattern syntax.

  • New parts: :name for one path segment, (de|en) for one of the listed words, and {/}? for an optional part.
  • A pattern without a ? matches the path alone. /checkout now also matches /checkout?step=2, and a query pattern written without ?, such as *cl=basket*, stops matching.
  • Invalid patterns are refused on save, such as a pasted regular expression. A stored one is skipped at ingest, and the settings mark it while you type.
  • URL rules are numbered and move up and down, since the first match wins.

Docs: Checkout funnel → Pattern syntax.

NewDashboard

Bots over time on Analytics#

"Bots" beside the chart type splits the pageviews chart into humans and bots, each bot, or bot type. The bot filter moves along and goes back when you switch the view off. Each stack can be hidden in the legend, and the view is part of the URL. Docs: Analytics.

ChangedDashboard

Fetch/XHR: one endpoint over time#

An expanded endpoint shows its own p75 or p95 over the period, with its calls, release markers, and the period before. It stays in the address bar, so the URL links to what is open. The "Over time" chart and the NEL and CSP charts draw a line per series with a fading tint, and each series can be switched off in the legend.

ChangedDashboard

Prod before dev#

The command palette lists prod applications and sites before dev ones among equally good matches. The dashboard opens on a prod site when you have not picked one before. On the API, each site hit of GET /search carries its application's environment. Docs: Analytics → Command palette.

ChangedDashboard

Network Error Logging reports as a table#

The Network Error Logging page reads more directly.

  • Reports is a table with time, type, status, time to failure, request, page, device, browser, visitor, and country. It loads as you scroll, sorts by any column, and copies as Markdown or CSV.
  • "Add type" offers every known error type, those of the period first.
  • Connection failures filter by type with + and -.
  • The "likely failing" warning on a server address is gone.

Docs: Network Error Logging → The page.

ChangedPlatform

Usage counts each pageview once#

Your usage, the quota banner, and the plan page counted beacons, so a pageview with several beacons counted more than once. They now count each stored pageview once, bots included. A sampled pageview counts as stored, not scaled up, so the figures drop to your real pageviews. Docs: Organizations → Plans.

RemovedAPI

Usage routes answer `pageview_count` only#

GET /organizations/{org_id}/usage and GET /organizations/{org_id}/partner/usage answer pageview_count and time_range. beacon_count, total_beacons, period_start, and period_end are gone, also from the per-site and per-client rows. A client that reads them has to switch.

ChangedDashboard

The visitor dialog refreshes#

The visitor dialog has a refresh button next to "Open in Explorer", so a visit still going on picks up its new pages. Orders, checkout, and cart show on the visitors page only for a shop. Docs: Analytics → Visitors.

ChangedDashboard

The visitors list says how each visit ended#

The Visitors page shows what happened in a visit, not only how many came.

  • Two new columns. Outcome says ordered, left the checkout, cart, browsed, or bounced. Journey draws one block per page, its width the time spent and its colour the page type.
  • Segment chips above the list set the behavior filters: with errors, slow, ordered, reached checkout, cart activity, one page only. Each shows how many visitors it lists.
  • The summary adds the share of visitors with a JavaScript error, slow visitors, and buyers.
  • A visitor opens in a dialog, on a phone as a sheet from the bottom. A click on a page opens its full detail, and "Open in Explorer" lists all of the visitor's pageviews.

Docs: Analytics → Visitors.

ChangedPlatform

Fetch and XHR calls are kept for a quarter of visitors#

An application stores the fetch and XHR calls of 25 % of its visitors by default. Every application that stored them for all visitors was moved to 25 %.

  • Counts stay comparable. Calls and errors are scaled back up, while percentiles and rare endpoints rest on fewer calls.
  • The rest of the pageview is stored in full, and cart interactions count at the pageview rate.
  • Set it back to 100 % under Visitors & sampling, or with collection.fetch_xhr_sampling_rate. It never applies above the traffic rate.

Docs: Applications → Fetch and XHR sampling.

ChangedAPI

The sampling recommendation rests on traffic alone#

GET /applications/{app_id}/sampling-recommendation recommends the rate that keeps about 20,000 pageviews a day, rounded up to the next 0.05 and never below 0.2. It drops binding_constraint, constraints, inputs.quiet_hour_pageviews, inputs.smallest_slice, and inputs.checkout, and adds inputs.pageviews_per_month. A client that reads the removed fields has to be updated. Docs: Applications → Recommended rate.

ChangedDashboard

Application settings in five sections#

The application settings are now General, Data scope, Visitors & sampling, Shop & page types, and Collector.

  • The navigation shows the value in force for each section, so you can check a setting without opening it.
  • One save bar counts your unsaved changes and can discard them. When something blocks saving, it takes you to the field.
  • The session ID moved to Visitors & sampling. Turning it on asks you to confirm first, because it writes to the visitor's device.
  • Site settings use the same rows and save bar. Old links to "Data & privacy" open Data scope.
ChangedDashboard

The pageview detail has fixed places#

Every value of a pageview now has a fixed place: eight facts and six vital tiles at the top, nine collapsible sections below. Each section shows a one-line summary, and the field search opens the section that holds a value. JavaScript errors take the full width, with the stack folded except on the first error. Docs: Analytics → The Explorer.

ChangedDashboard

One notice bar#

Notices share one slim bar at the top instead of stacked strips. "Details" opens all of them at once, the most urgent first. On a phone the bar shows the most urgent title and how many more there are.

NewPlatform

Server-Timing: the time in your own code#

The new phase code_dur holds the time your backend spends in its own code, outside the database, key-value, HTTP, and render layers. It comes from fm-code, or from the Tideways layer unknown that the Shopware plugin passes through. The dashboard shows it as "Code" beside DB, KV, HTTP, and Render. Pageviews from before the change keep unknown as a custom key. Docs: Server-Timing → Backend phases.

ChangedPlatform

Search pages keep the search term#

On a page typed search, the stored URL keeps the value of q, search, and OXID's searchparam, cut to 64 characters. A category page keeps searchparam for OXID's filter form. This holds in every mode and needs no opt-in, so your privacy notice has to name site search terms. Docs: Privacy → Site search terms.

ChangedAPI

Checkout stage events and estimated fields#

Every checkout pageview writes a stage event, so /analytics/events and the checkout journeys return more rows.

  • Names: view_cart, begin_checkout, and order_placed, the same names the Shopify pixel uses. source is collector or shopify_pixel.
  • meta.sampling.estimated_metrics names the fields of a funnel, live, NEL, or CSP response that are estimates. It is empty while nothing is sampled.
  • On the NEL routes meta.sampling describes the browser's failure sampling instead of the application's rate.

Docs: Checkout funnel → Stage events and sampling.

ChangedPlatform

The checkout and the live view count every visitor#

The checkout funnel and the purchases now count every visitor at any sampling rate, so a shop can run sampled again.

  • Stage events are written for every checkout pageview, also for visitors sampling drops. The funnel steps, cohort sizes, stage series, and purchases read them.
  • Live numbers count every visitor in the window, sampled or not, on the Shopfloor and in /analytics/live.
  • Estimates carry a ~ in the dashboard, such as the cart figures and last week's numbers. Checkout steps and orders stay unmarked.
  • Events is in the Explorer for every shop, not only on Shopify.

Docs: Checkout funnel → Stage events and sampling.

September 2026

NewDashboard

Content Security Policy: what your policy blocked#

Errors → Content Security Policy shows what your site's policy blocked, and what a report-only policy would block. It needs a policy that names fastmon as its report endpoint.

  • Two tabs: Overview and Reports. The overview stacks violations over time by directive or by policy, with cards for what was blocked, pages, scripts, directives, sites, countries, devices, browsers, OS and traffic.
  • The Sites card is the setup check. Each site shows its latest report, and a site without one says so. Without any report, the page links to the setup guide.
  • The + filters to a value, the - excludes it, and the magnifier opens its reports. Blocked or report-only, directive, blocked URL, page and script stay in the address.
  • Reports list every CSP report with the policy it broke. Each has its own share link and links to the visitor.
  • A visitor's journey shows their CSP reports between the pageviews, blocked in red and report-only in amber.

Docs: Content Security Policy → The page.

NewPlatform

fastmon collects Content Security Policy reports#

A policy whose report-to and report-uri name https://fastmon.site/c/v/{collector_hash} makes the browser report every violation to fastmon. Run a new policy as Content-Security-Policy-Report-Only first to see what it would break.

  • Both formats arrive, the Reporting API and the older report-uri, so practically every browser reports. Counts are exact and not sampled.
  • The report's host has to be a site of the application, or a subdomain of one. A report never creates a site.
  • A report keeps the disposition, directive, blocked and source URL without their query, the page, and the policy with nonces blanked. Identical reports of one upload are stored once with their count.
  • Dropped: the code sample of 'report-sample', reports caused by browser extensions, and a referrer from another host.
  • Shopify storefronts cannot set the headers.

Docs: Report Content Security Policy violations · Privacy → CSP reports.

NewAPI

Read CSP reports through the API#

POST /analytics/csp counts the reports and affected visitors per directive and disposition over time. /analytics/csp/breakdown groups them by one dimension, such as the blocked URL or the page, and /analytics/csp/reports lists them one by one with the policy they broke. The visitor detail and the checkout journeys carry csp_reports beside nel_reports. The filters disposition, effective_directive, blocked_url and source_url work on the CSP routes only, every other route answers 422. Docs: Content Security Policy → API.

ChangedDashboard

One plan to buy: fastmon One#

The plan page no longer asks you to choose a plan. The 29 EUR tier is withdrawn, and everything it left out is part of fastmon One.

  • Price and quotas stay: 99 EUR net per month with 1,000,000 pageviews and one production application. Development applications stay free.
  • Capacity is the one choice left: extra pageviews in steps of a million and production application slots, next to your usage and forecast.
  • One price summary lists every item at full price, each discount as its own line, the change against today and the amount including VAT. Missing billing details open as a form on the page.

Docs: Organizations → Plans.

ChangedAPI

The plan value is `one`#

Every response that carries a plan answers one where it said standard, and light is gone from the enum. There is no deprecation window, so a client that matches on "standard" stops matching without an error. GET /organizations/{id}/billing/plans drops early_adopter, and GET /organizations/{id}/billing reports both add-on amounts net of the yearly discount. Docs: Organizations → Plans.

ChangedDashboard

JavaScript errors as a share, the top five, and last seen to the minute#

The chart on JavaScript errors separates an error spike from a traffic peak.

  • The affected pageviews chart switches between count and %. The share is the part of each bucket's pageviews that had an error.
  • Top 5 draws the five errors that reached the most pageviews as lines beside the total. The legend shows their values for the hovered bucket and links to each error.
  • Dragging across the chart zooms in. Both choices stay in the link, and an error's own page has the same % switch.
  • "Last seen" is exact to the minute, on the list and in its ordering. Every row of POST /analytics/error-types carries last_seen_at.

Docs: Analytics → Error Analytics.

NewDashboard

The time range picker zooms, drags and jumps to now#

The range pill and its picker take fewer clicks.

  • Minus and plus double or halve the window, also on the - and + keys. A live window stays live.
  • Drag across a trend chart to zoom into that stretch. Under six hours the charts reload by minute.
  • Jump to now moves a past window to the present at the same length.
  • "By minute" works on long windows and cuts them to their last 6 hours. The arrows then step 6 hours at a time.
  • Resolution is a row of tiles, each with the number of intervals it gives, or why it does not fit.
  • Date and time are fields of their own and apply at once, without an Apply button. The expression field takes a clock range such as 14:00-16:30.
  • A timeline at the top shows the window on the days around it. Drag it, pull an edge, or scroll and pinch to zoom.

Docs: Analytics → Time range.

NewPlatform

Label pages by URL#

page_type.url_rules takes up to 10 URL patterns, each with a page type, and the first match wins. It is for a shop whose body classes say nothing, such as a headless storefront, and needs no tracker change. In the dashboard they are the URL rules under page-type detection.

  • The sources come in this order: the header, your body classes, your URL rules, the platform ruleset, the Shopify routes, then the checkout stage.
  • A cart, checkout or success path gives a page type when nothing else did: cart for the cart, checkout for the other two. Pageviews stored before 2026-09-29 keep their empty page type.
  • GET /applications/{app_id}/page-type-sources lists an application's sources in that order, each with the page type it stores. Viewers may read it.

Docs: Applications → Page-type detection.

ChangedPlatform

OXID works from its `cl` controller#

On an OXID application the stored URL keeps the cl controller, as in /index.php?cl=thankyou, without collect_query_values_for. The URL reports show one row per controller from 2026-09-29 on, and other query values are still dropped.

  • The checkout funnel needs no custom paths. basket marks the cart, user, payment and order the checkout, thankyou the success. Your own *?cl=* patterns still come first.
  • The page type comes from the controller when the header, your rules and the body class gave none. details is product, thankyou is checkout.

Docs: Checkout funnel → OXID.

ChangedDashboard

Checkout: conversion as a trend, and orders by name#

The checkout numbers on the analytics page read like a shop report.

  • Conversion is a metric above the main chart, drawn against the previous period. Its tooltip names the visitors behind each point, the share who used the cart, whose cart failed, and the time to purchase.
  • Completed checkouts are called orders. The last funnel stage reads "Ordered" in the filters, the chips and the Explorer.
  • Business Impact shows the same conversion rate, buyers over all visitors. It used to divide by the visitors who reached a cart.
  • The checkout card names the visitors whose cart broke and how long a purchase takes, as the median and the p90.
  • Buyers compared is one table at the bottom of the page, closed until you open it. It compares buyers with visitors who reached the checkout without a seen purchase, per funnel stage.
  • The checkout numbers follow the page's filters, Orders included, except filters that pick a checkout stage.
  • They show only on a shop. An application whose platform is unknown counts as one until you mark it as not a shop.

Docs: Checkout funnel → Where it shows up.

NewDashboard

Group by HTTP status#

The Explorer and POST /analytics/query group pageviews by status_code, so error pages such as 404 or 500 form their own series. Pageviews whose browser reports no status, mostly Firefox and Safari, form the "Not reported" group, 0 on the API. Notification rules filter on status_code too, and the Explorer's group-by picker has a search field. Docs: Analytics → The Explorer.

NewAPI

Good, needs improvement and poor per bucket#

POST /analytics/query returns the share of pageviews rated good, needs improvement and poor for lcp, inp, cls and ttfb, as good_share, needs_improvement_share and poor_share. Each bucket's three shares sum to 1 over the pageviews that reported the metric, with the thresholds of the Experience Score. A slow tail shows up per hour where the p75 barely moves. Any other metric in the same request answers 422.

ChangedAPI

The previous period keeps the weekdays#

compare_to: previous_period now lies the fewest whole weeks earlier that cover the window, at least one. Friday to Monday compares with Friday to Monday a week before, and 30 days go back five weeks.

  • Up to 7 days it equals previous_week. The last hour against the hour right before it is no longer available.
  • compare_to: previous_day compares with the same window 24 hours earlier, on /analytics/query and the checkout funnel. The dashboard does not offer it yet.
  • The dashboard compares the same way, including "New" and "Rising" on the errors list, pages per visitor and the Experience Score on Business Impact.

Docs: Analytics → The dashboard.

FixedAPI

Release comparisons work again#

A query with compare_to_release_id answered 500 whenever the release fell inside the window, and now returns the before and after split. A released_at sent with a UTC offset such as +02:00 is stored at the right instant. Releases created that way before the fix keep the wrong time until you edit them.

ChangedPlatform

Percentiles over long per-URL windows#

A per-URL table with many percentiles, such as the response breakdown over 90 days, no longer fails on sites with a few thousand pages. Percentiles from single pageviews now use the method of the aggregate tables, capped at 30 s for the timing metrics. Such a percentile can move slightly against earlier numbers, and it no longer depends on which table serves the window. Alert rules keep the uncapped method.

ChangedAPI

The MCP error tools name the script in each frame#

get_errors and get_error_detail name the script path in every stack frame, as in www.example.com/js/theme.js:1:77801. A frame on a shared host then shows whether it sits in your bundle or in a third-party script. get_errors returns up to five frames per signature instead of three, and a signature's message no longer comes back empty when some rows were collected with error messages off. Docs: MCP → What the model can read.

NewDashboard

Network Error Logging: failed requests as the browser reports them#

Errors → Network Error Logging shows the failed requests that Chromium browsers report for your site, including pages that never ran the tracker. It needs the NEL headers on your site.

  • Three tabs: Overview, Paths and Reports. The tab is part of the address, so a link opens the same view.
  • HTTP errors come first, server errors above client errors. Affected visitors lead every number, with their share of all visitors, and the reports come second.
  • "Where they fail" shows every dimension as a card: status codes, pages, countries, sites and hosts, server addresses, devices, browsers, OS and traffic. An opened card takes the whole row.
  • Sites and hosts is the setup check. Each site shows its latest report, and a site without one says so. When most reports without details point at one server address, the card names it as likely failing.
  • Connection failures have their own section, closed by default. It compares them with their usual level and flags a broken certificate and a rise in refused or timed out connections, unknown hosts or reports without details.
  • Reports list every NEL report with its class and fold a burst from one visitor on one page into one line with a count. Each opens to every stored field, has its own share link, and links to the visitor's journey.
  • A visitor's journey shows their NEL reports between the pageviews, in time order, marked as such and with the error type in plain words.
  • Abandoned loads stay hidden on the page and on the journey, each with a switch to show them.
  • The + next to a value filters to it, the - excludes it, and the magnifier opens the row's reports. The 5xx and 4xx tiles filter a whole status class. The filters stay in the address and sit in the Filter panel under "Only on this page", marked by a page icon on the Filter button.

Docs: Network Error Logging → The dashboard page.

ChangedDashboard

Errors is a group in the sidebar#

JavaScript errors, Failed resources and Network Error Logging are three pages under Errors, each with its own address, breadcrumb and entry in the command palette. Old links to a tab open the new page with the time range and filters they carried. Docs: Analytics → Error Analytics.

NewAPI

NEL reports carry a class and the server address#

Two new fields on every report help you read what failed and where.

  • nel_class says what a report means: http_error, connection, abandoned, downgraded or other. It is derived when the reports are read, so older reports carry it too.
  • server_ip is the address of your server that the failed request went to. Only a public address is stored, and reports from before 2026-09-28 have none.
  • Both are filters with an exclude and breakdown dimensions on POST /analytics/nel.
  • A rise of downgraded on a site with two addresses points at one failing address, and server_ip names it.
  • Two long Chromium error types are no longer dropped. An error type may have up to 64 characters now.

Docs: Network Error Logging → Several server addresses.

NewPlatform

fastmon collects Network Error Logging reports#

Two response headers on your site make Chromium browsers report every failed request to https://fastmon.site/c/n/{collector_hash}. That covers DNS failures, refused connections, timeouts and 5xx pages that never ran the tracker.

  • Chromium only. Safari and Firefox send no reports, so every count is a lower bound.
  • The report's host has to be a site of the application, or a subdomain of one. A report never creates a site.
  • A report keeps the error type, phase, HTTP status, time to failure, method, protocol, the reduced URL, the page it came from and the browser's major version. It also keeps the visitor identifier under store_stitch and the edge labels, and drops the User-Agent.
  • Cloudflare's own NEL setting replaces your headers, and Shopify storefronts cannot set them.

Docs: Set up Network Error Logging and Privacy → NEL reports.

NewAPI

Read NEL reports through the API#

Three routes under /organizations/{org_id}/analytics/nel read the reports, with the site scope, visitor and edge label filters of the other analytics routes.

  • POST …/nel returns the weighted count, the series, the counts per error type and status code, and the most affected host and path pairs. affected_visitors counts distinct visitors where the site stores stitch, and elapsed_by_error_type gives the p50, p75 and p95 time to failure.
  • POST …/nel/breakdown groups the reports by one dimension, such as page_url, host, browser_version or site_id. A site_id row carries last_seen_at, its latest report.
  • POST …/nel/reports lists single reports newest first, paged by cursor, and filters.report_id selects one. visitor_id links a report to the visitor's journey.
  • Filters of their own: error_type, status_code_range, host, path and page_url, all but the range with an exclude. Every other analytics route answers them with 422.
  • Without an error_type filter, abandoned counts too. Exclude it to hide visitors who left while something loaded.
  • The visitor detail and the checkout journeys return nel_reports beside pages and events, up to the latest 100 per visitor.
  • Renamed on 2026-09-28. Until then the routes were …/network-errors, the class network_error_class and the list network_errors. The old names answer 404.

Docs: Network Error Logging → API.

NewTracker

The tracker sends the HTTP status of the page#

The beacon now carries the document's HTTP status as rs, read from the navigation entry's responseStatus. A pageview of an error page that still loads the tracker can be told apart.

  • Unknown is 0, never 200. Firefox, Safari and soft navigations report no status, and a value outside 100 to 599 is stored as 0.
  • status_code is on every row of POST /analytics/beacons, and metric_ranges takes it: {"metric": "status_code", "gte": 400} lists error pages. MCP and Ask accept the same filter.
  • The Explorer shows the status as a column under "Network & context", and HTTP status >= 400 in the filter bar lists error pages.
NewDashboard

The Shopfloor: your shop live, from above#

Analytics → Shopfloor draws your shop as zones by page type, with every recent visitor as a dot on the page they are on.

  • Visitors move as the data does. A new visitor walks in from their traffic source, a click walks across the floor, and a sale dashes to the exit, with an optional ka-ching.
  • The window is 5, 15, 30 or 60 minutes. The headline, the orders and every zone's figures follow it.
  • Today's visitors, orders and conversion rate count from your own midnight, each beside the same weekday last week. A timeline under them shows visitors per hour with a forecast for the day.
  • Click a zone to walk into it, with its page types and its top 10 URLs, each opening in the Explorer.
  • Built for a wall screen. ?window=60&sound=1&kiosk=1 opens it set up, and on a phone the floor turns upright.
  • It needs store_stitch, and a site without it says so. Bots follow the traffic scope, and the filter offers country, device, page type and traffic.
  • The API behind it is POST /analytics/live, POST /analytics/live-summary with an optional timezone, and GET /platforms/{platform}/page-types, which sorts a platform's page types into groups shared by every platform.

Docs: Analytics → Shopfloor.

NewPlatform

Cache TTL beside cache age#

fastmon now collects how much freshness a cached page had left, per tier, as cdn_ttl and origin_ttl in seconds. Age plus TTL is the configured lifetime.

  • CDN vendor keys work without a change on your site: cdn-cache-age, cdn-cache-ttl, origin-cache-age and origin-cache-ttl, each with the seconds in desc. They no longer show as a server phase, and the fm-* keys win when both are sent.
  • The Cache page shows freshness per tier as a bar of age and TTL left, with p50, p75 and p95, per page type too. A chart of median age and TTL over time shows a purge as the age dropping to zero.
  • The Explorer offers CDN age, CDN TTL, origin age and origin TTL as metrics, columns and filters. POST /analytics/server-timing returns a cache_ttl block, and notification rules, MCP and Ask know both TTLs.

Docs: Server-Timing → Cache TTL.

ChangedDashboard

The Cache and Server-Timing pages read more clearly#

Several changes make both pages easier to read and filter.

  • Age and TTL columns are medians, matching the cache freshness card.
  • The cache status card and table filter on hover, plus to keep a status and minus to exclude it. The table splits by page type on request.
  • An opened row in the Server-Timing phases table shows every phase over time, scoped to that row.
  • The origin hosts card shows each host's share of the traffic against an even split.
  • The trend legend shows average, max and last value per line, and a click hides or shows a line.
  • Fixed: the trend split by host or login runs to the end of short ranges again, and hit rates in the Explorer no longer read 0% with a percentile selected.
ChangedDashboard

The traffic chart stacks humans, likely bots and bots#

The traffic chart stacks pageviews into the three traffic classes, so every pageview counts once and the bands add up to the total. Before, bots and datacenter traffic were two overlapping series, and a Googlebot pageview was drawn twice. The visitors line always counts real visitors, and the chart has a share link to ?panel=traffic.

ChangedDashboard

Prod applications come before dev#

Where an application exists as prod and dev under one name, prod now comes first on the applications page, in the site switcher and in the command palette. Otherwise the application and site lists return the newest entry first, and paging through the site list no longer repeats or skips a site.

RemovedAPI

`POST /organizations/{org_id}/sites` is gone#

The route required an application_id that no client sent, so it had no working caller. Add a domain with POST /applications/{app_id}/sites instead. GET on the organization path is unchanged.

ChangedPlatform

A CDN cache hit no longer counts replayed origin timings#

A CDN replays the origin's Server-Timing header on every hit, so a page served from the edge showed the backend time of the request that filled the cache. On a pageview with the CDN status HIT, STALE, UPDATING or REVALIDATED, the origin phases and custom timed keys are no longer stored or counted. edge_dur, the cache statuses, ages and TTLs still count, so origin percentiles can move and counts drop on well cached sites. Docs: Server-Timing → Backend phases.

NewAPI

Minute buckets on the series endpoints#

granularity=minute works on the checkout stages, checkout funnel, error and failed resource detail and stitch comparison series, for windows up to 6 hours. A longer window answers 422, and auto still answers hourly up to 48 hours. The checkout chart uses it on the 15 min to 6 h ranges and shows a curve instead of spikes at the full hour.

NewAPI

An application can store a share of its visitors#

collection.sampling_rate on the application sets the share of visitors whose beacons are stored, 1.0 by default.

  • The edge decides once per visitor, so a visitor's pageviews are kept or dropped together. A new rate applies within about a minute.
  • Traffic counts are scaled back up on every analytics endpoint, in Ask, in MCP and in the weekly mail. Percentiles, shares and minimum-sample thresholds stay on the stored rows.
  • Stored rows still drive the Beacon explorer, the funnel stage counts and the histograms.
  • The lowest rate is 0.1. The API refuses anything below it. Only an application that stores stitch is sampled, and checkout events never are.
  • meta.sampling on every analytics response names the lowest rate configured in scope and whether it is below 1.0.

Docs: Applications → Traffic sampling.

NewDashboard

Geographic distribution as a globe#

The section draws the world as a globe or a flat map, beside the country list.

  • The globe opens on your traffic. A shop selling into one market lands on that market. Drag turns it, the wheel zooms, and the arrow keys, plus and minus work too.
  • The flat map keeps the poles in proportion, so Greenland no longer looks like a continent. Zoom and reset buttons sit on the map.
  • The chosen view stays in the URL, so a reload or a shared link opens the same one.
  • Clicking a country shows its filter buttons. The highlight follows the pointer and no longer sticks after you leave.
  • The section has a heading on the dashboard.
FixedPlatform

Shopify checkout events join the storefront visitor#

A Shopify pixel's checkout events now carry the same visitor identifier as the shop's pageviews. The funnel no longer counts a buyer twice, and the visitor's journey shows the checkout. Events collected before the change stay detached.

At the switch every visitor identifier changed once, as it does at the daily salt rotation. An application serving several domains now counts a visitor once across all of them. Docs: Privacy → stitch.

NewDashboard

A visitor's journey shows the Shopify checkout#

Checkout started, payment submitted and order placed now sit between the pageviews, in the order they happened. An event from before the selected window shows with a negative offset. Grouped by session, the journey says why its checkout half is missing.

FixedDashboard

The visitors list shows every visitor again#

The list applied an "at least 3 pages" filter nobody had set, so one- and two-page visitors were missing. All filters now start empty, and the page-count filter still works when you set it.

ChangedAPI

Every API field follows one vocabulary#

One name, one type and one scale per concept, across responses, request bodies and query parameters alike.

  • Time is timestamp. Every series point, event and journey row answers it, in place of at, t, bucket and ts.
  • Shares are fractions, 0 to 1. share, with_error_share, relative_change and progress replace percentage, percentage_of_total, share_pct, change_percent and percent.
  • First party versus third party is one field, ownership, with the values first_party, third_party and mixed. It replaces bucket, is_first_party and by_bucket.
  • Durations and counts carry their unit and their noun: duration_ms, p75_ms, time_to_success_seconds, error_count, frame_count, sample_count.
  • Closed vocabularies are enums, among them the funnel stage, granularity, device_type and identity. A generated client gets a type rather than a free string.
  • No response field is nullable. 444 declared T | null for a value the API never sends, because a field with no value is left out. Nothing on the wire changes, and a generated client now sees an optional field.
  • Each deprecated name points at its successor through x-replaced-by in the OpenAPI document, so a migration can be driven from the spec.

Docs: API → Analytics.

ChangedAPI

An analytics response separates result, request and paging#

A response used to mix the answer, the paging and an echo of the request in one flat object.

  • meta carries the facts about the answer: execution_time_ms, and for a list total, limit, offset and has_more in one place.
  • request carries what the server actually used: the window with a relative range already resolved to its two ends, the bucket width, the identity and the filters.
  • Every list answers its rows under data. meta.total is the rows the list matched before paging, everywhere.
  • A sum over a top-N has a name of its own (frame_count, failed_count, resource_count, size_bytes, beacon_count), so it is never read as the row count.
  • All twenty analytics responses carry the two objects, including POST /analytics/query and POST /analytics/histogram.
RemovedAPI

The deprecated API names no longer answer#

228 of them, across analytics, synthetic, notifications, applications, issues and partners. Each had a replacement served since the release that deprecated it, and each is out of the OpenAPI document, so a generated client stops offering it.

  • A request that still sends an old body key is refused. A stale id, bucket, first_party, range_days or mute_until is a 422 naming the key, rather than a write that vanishes.
  • The usage window keeps its old names for two more releases. period_start, period_end and total_beacons on GET /organizations/{org_id}/usage and on the partner usage route still answer, because usage:read is a customer scope.
  • The application keeps six flat fields for the Shopware plugin: source_hash, collector_hash, proxy_secret, collector_mode, collector_endpoint and pagetype_ruleset.
NewDashboard

The Shopify checkout in the dashboard#

  • The checkout chart counts the Shopify checkout. The stage counts come from the checkout events as well as the page URLs, so the chart agrees with the funnel card below it instead of sitting empty above it.
  • All three stages are compared against the previous period the same way. They were three separate queries, and a comparison that arrived for one and not the others made the funnel look measured to different standards.
  • A Shopify application points at its pixel guide, prefilled with its own hashes. The embed snippet of any application disappears once its first beacon arrives.
  • The card says when the last checkout event arrived, so you can see that the pasted pixel reports. It warns first where the visitor signal is off, because the events are unusable without it.
  • Visitors who submitted payment show beside the completed checkouts, never as a step of their own. Completed checkouts on a Shopify shop are labelled a lower bound.
  • A step that comes out above the one before it explains itself. On Shopify that is routine: a buyer who goes straight into the checkout never loads a cart page.
  • A funnel grouped by session says why its checkout half is missing, because the pixel events carry no session id. Rotating an application's hashes now warns that it breaks an installed pixel.
  • Fixed: the measured conversion rate on the business page was never shown. It read a field the API had renamed, so the page fell back to its estimate even for a shop whose funnel was reporting.

Docs: Checkout funnel → Shopify checkout events.

NewDashboard

Explorer lists the events behind the numbers#

  • An Events page in the Explorer, one row per event the tracker reported. Filter by type, page through them, and open the visitor a row belongs to.
  • It appears for an organization whose applications can produce events, which today means a Shopify shop and its three checkout events.
  • The funnel links to it, so "is this number right" has somewhere to go. An empty list says nothing arrived in the window, not that a pixel is missing.
  • On the API the same rows come from POST /analytics/events, newest first and cursor paginated.
RemovedAPI

The application's flat fields are gone#

The flat twins of the grouped settings no longer answer. Read them from collection, shop.checkout, page_type, script, site_defaults and collector.proxy_secret, which have carried the same values since 2026.9.27.

  • Removed: collect_sessions, collect_fetch_xhr, collect_error_messages, collect_error_frames, collect_query_keys, collect_query_values_for, session_consent, template_url_ids, checkout_tracking, the three checkout path lists, pagetype_rules, source_version, data_retention_days, store_stitch, count_visitors_by and the two proxy-secret fields.
  • Create and update reject any key they do not know. A removed flat field is a 422 naming it, so a settings write is never dropped in silence.
  • That covers read-only fields echoed back from a GET, such as id, source_hash, site_count or created_at. A read-modify-write client has to send only the fields it changes.
  • The Shopware plugin is unaffected. Its six fields stay flat, and sending collector_mode and collector_endpoint flat or under collector are both still accepted.

Docs: Applications → Application fields.

NewAPI

The Shopify checkout reaches visitors, journeys and a stage trend#

  • The site status reports checkout events, so a pasted pixel becomes visible. events_today, last_event_at and events_available sit beside the beacon fields, and a non-null last_event_at proves the pixel reports.
  • A visitor's journey carries the checkout. POST /analytics/visitors/detail answers events beside pages, with the name, the funnel stage, the source and the time of each. Interleave the two arrays by time.
  • The visitors filter finds a Shopify buyer. contains_checkout_stage counts a checkout event as reaching the stage. Each row is still a pageview row, so a visitor known only from events has none to list.
  • New POST /analytics/funnels/checkout/stages reports the visitors who reached cart, checkout or success per hour or day, events included. A compare_to reaching behind the day the pixel was installed shows the installation as a rise.
  • New POST /analytics/events lists the events themselves, newest first and cursor paginated, so a test order can be held against the Shopify order list. A filter with no column on the events table is a 422 naming it.

Docs: Checkout funnel → API.

NewTracker

The Shopify checkout reports its own steps#

Shopify runs its checkout on its own infrastructure, where your theme script never loads, so the funnel ended at the cart.

  • Paste one snippet in the Shopify admin, under Settings → Customer events → Add custom pixel. It reports three steps: checkout entered, payment submitted, order placed.
  • The dashboard links to the guide with your application's hashes in the URL, so the snippet there is ready to paste.
  • On a first-party collector the pixel is first-party too. The script loads from your own host, so the checkout makes no third-party request. Your proxy has to relay /s/e/* and /c/e/*.
  • It measures nothing else. No Web Vitals, no page load and no errors come from the Shopify checkout.
  • An event carries the step, the visitor's country, device, browser and traffic class, and the edge identifier. No URL, no session id, no order value, nothing Shopify knows about the customer.
  • store_stitch has to be on. Without the edge identifier the checkout numbers stay empty.
  • Set Permission to "Not required" and Data sale to "does not qualify". On Shopify's defaults the pixel waits for marketing consent, and the two halves of your funnel then cover different populations.
  • Rotating the application's hashes stops the events. The old script URL answers 404 silently, so paste the new snippet after a rotation.
  • Content blockers stop the pixel. EasyList's EasyPrivacy list blocks Shopify's custom-pixel sandbox path, and uBlock Origin ships it by default. Those visitors send storefront pageviews and no checkout events.
  • Purchases are a lower bound. Shopify does not fire its checkout completed event for most Shop Pay and Apple Pay orders.

Docs: Track the Shopify checkout.

ChangedAPI

The checkout funnel counts Shopify checkout events#

The funnel reads the pixel's events beside the pageviews, which moves a few numbers for a Shopify shop.

  • An order reaches success and an entered checkout reaches checkout, with no pageview on either. Visitors seen only in the checkout join visitors_total, so the conversion rate stays at or below 100 %.
  • New payment_submitted_visitors: visitors who submitted payment, a fact beside the steps rather than a step. It is absent, not 0, where no event was read.
  • Payment without a success most likely means a wallet order. That is what makes the success step a lower bound.
  • A cart to checkout rate above 100 % is normal now. A buyer who went straight into the checkout never loaded a cart page, and step rates stay unclamped.
  • A filter the event rows cannot answer leaves them out: URL, page type, campaign, a metric threshold.
  • The cohort comparison counts only visitors with a pageview. A buyer whose whole run is checkout events shows in the success step and in no cohort.
  • The sessions explorer, the session detail and the journey view still read the stage off pageviews.

Docs: Checkout funnel → Shopify.

ChangedDashboard

One word per thing#

The same number went by several names, in both languages. Every concept has one word now.

  • A page load is a pageview, a person is a visitor. The dashboard also said page views, views, screen views, visits and unique visitors.
  • Labels that named the wrong number are corrected. "Views per visit" divided pageviews by visitors and is now "Pageviews per visitor".
  • German tells a Site from a page. A Site stays a Seite; a page in a list, a column or a count is a URL.
  • One rating scale: Good / Needs improvement / Poor, in German Gut / Verbesserungswürdig / Schlecht.
  • One word for the measured span. It is a period, not a window or a range.
  • Labels are sentence case. Product and spec names keep their capitals: Core Web Vitals, Largest Contentful Paint, Speed Index, Service Worker, Early Hints, Best Practices.
  • Empty charts say "No data in the selected period". The old wording named visitors on charts that do not count them.
ChangedDashboard

The error list groups a fault's variants into one row#

One broken script used to fill four of the top five rows. Browsers report the same place in the code a few characters apart, and a second caller is a second stack.

  • A row is a fault, not a signature. It counts every variant and says how many signatures it stands for.
  • "New", "Rising", the sparkline and the affected pageviews cover the whole group. A row no longer reads as new when its biggest signature changed.
  • A signature's page switches between the variants of its fault. Stored fingerprints do not change and nothing regroups.
  • POST /analytics/error-types takes group_variants, off by default. A grouped row carries the summed count and lists every fingerprint in variants.
  • Match rows across periods by their variants. The row's own fingerprint is its biggest variant and can change.

Docs: Analytics → Error Analytics.

NewAPI

An application says what it runs on#

An application now records its platform, whether it is a shop and whether it is a single-page app.

  • platform holds the shop system or CMS, for example Shopware 6, Shopify or PrestaShop. The last two could not be stored before, having no body-class ruleset.
  • A platform you set is manual, and later detection leaves it alone. The object carries key, source, detected_via and detected_at.
  • The body-class page-type read is its own switch, page_type.body_class. Picking a platform does not turn it on, and pagetype_ruleset is derived from the two.
  • shop.active says whether an application is a shop, with shop.source for where that came from. Without a setting it follows the platform.
  • spa marks a single-page app, set by hand and off by default. Nothing reads either flag yet.
  • The response groups its fields: page_type, shop.checkout, collection, collector, script and site_defaults. Create and update take the same groups as partial objects.
  • The flat fields are deprecated. They still work, and an API client should move to the groups.
  • Saving from the dashboard sends only what you changed.

Docs: Applications → Application fields.

NewPlatform

Shopify pageviews get a page type from the URL#

A soft navigation carries no document header, and Safari before 16.4 does not expose one. Those Shopify pageviews had no page type at all, and the collector now reads it from the URL. The label is Shopify's own (product, collection, page, index, cart, search, blog, article), the same one a hard load reports. Rows already stored stay empty.

Docs: Server-Timing → Page type.

ChangedTracker

A repeated fetch/XHR failure sends one beacon#

The tracker sends a fetch_xhr_update for the first failure with a given status on an endpoint. Repeats only raise the error count, which the next beacon carries anyway. A polled endpoint that an ad blocker rejects used to cost up to 20 collector requests per pageview.

FixedTracker

A script the browser cannot parse is one error group#

Only Firefox's wording was recognized as syntax_unexpected_token, so the same broken script showed up once per engine. Chrome and Safari now get the same kind and the readable sentence with it. The affected SyntaxError groups get a new fingerprint and appear as new once. Where a site stores no messages the kind comes from the tracker, so old groups keep growing until browsers fetch the new bundle.

Docs: Tracker → What a message says.

ChangedAPI

The checkout funnel reports a real conversion rate#

The rate showed anything from 30 % to 130 %. It divided by the cart step, which a drawer cart, an express checkout or a deep link never produces.

  • The conversion rate divides buyers by all visitors. That is the number GA4 and Shopify report, against the usual 1 to 3 % benchmark.
  • visitors_total sits beside it, and every point of the series carries its own visitors. Bucket visitors do not add up to the window total.
  • FunnelStep.conversion_rate is now reach_rate. Same number, honest name: the share of the first step, which can exceed 100 %.
  • A conversion rate per time bucket, measured against that bucket's own first step. The series has a point for every bucket with traffic, so a broken day reads 0 %.
  • New POST /analytics/funnels/checkout/cohorts compares visitors with an observed success against those who reached the checkout without one. Per group: Web Vitals over the journey and per stage, cart calls, their latency and their failures.
  • checkout_no_success is not a count of lost purchases. A real buyer lands in it whenever the success page is invisible to the tracker.
  • Four more numbers: cart_interaction_rate (benchmark 5 to 6 %), cart_interaction_error_visitors, time_to_success_seconds (p50, p75, p90) and cart interaction kinds per point.
  • Read the first two as floors wherever the shop's cart calls are invisible to fetch/XHR collection.
  • Filters that pick funnel pages no longer narrow the denominator (checkout_stage, the cart thresholds). Audience filters (site, country, device, campaign) narrow both halves.
  • A visitor count that times out leaves visitors_total and conversion_rate out of the response. A rate that could not be measured is absent, never 0 %.

Docs: Checkout funnel → What the numbers mean.

ChangedDashboard

Bots are hidden by default#

Every page starts without bot traffic, so the numbers describe real visitors unless you say otherwise.

  • "Bots only" is a scope you can pick, next to "No bots" and "All", in the filter menu's new Traffic control. The two together are all of your traffic and nothing twice.
  • The scope reads in the page header next to the period: a crossed-out bot, a bot, or "incl. bots" in words.
  • It is not counted as a filter and survives "Clear all". The panels that are about bots keep showing them either way.
  • Every part of the filter pill opens the filter panel, the percentile, the device and the bot mark included.
ChangedDashboard

The maximized Explorer table takes the whole window#

  • Maximizing folds the sidebar away too. Only the header row with the logo, the filters and the account menu stays above the table.
  • Maximized is a reading mode, not a setting. It lasts as long as you are on the page.
  • From 1536 px up the query panel and the table toolbar move into the header row. Narrower, they stay in the page.
  • Copy, CSV and Maximize share the line that names the result, instead of taking a line of their own.
  • The raw pageview list says how many rows matched at the bottom, next to the page size and the pager.
ChangedDashboard

The command palette finds every page#

The menu knew the sidebar groups and not the pages inside them, so searching "fetch" found nothing.

  • Every page is listed with its group. Typing a group ("network") lists everything under it.
  • Weekly overview, fastmon AI, Partners and your account are in the menu, each under the condition that puts it in the sidebar.
  • Pages are found by what they are called elsewhere. "API" finds Fetch/XHR, "Largest Contentful Paint" finds LCP.
  • Opening an organization lists its applications first, above its domains. Picking one scopes the page you are on to that application.

Docs: Analytics → Navigating with the command palette.

FixedDashboard

The Analytics chart always starts as a line chart#

Picking an hourly range used to switch the chart to bars on its own, so the same page changed shape when you changed the period. The bar chart is still there, it just has to be chosen.

NewAPI

Which API calls fail where an error fires#

Built for the rejection that carries no message and no stack: the error says nothing, the failing request says which URL, method and status.

  • POST /analytics/error-types/correlated-requests asks an error signature which fetch/XHR endpoints fail in the same pageviews.
  • Every endpoint comes with the share it fails at in pageviews without that error. The list is ranked by the ratio of the two.
  • The answer says endpoints occur together, never that one caused the other.
  • Which failures count follows the error's message. An empty-response error matches cancelled calls, an invalid-JSON error matches 4xx and 5xx pages.
  • Applications with fetch/XHR collection off are left out rather than blanking the answer, and fetch_xhr_collected: false says so.
  • It reads raw pageviews, so it goes back as far as those are kept, not as far as the rollups.

Docs: Analytics → What broke, and where.

FixedPlatform

Fewer nameless API calls in a pageview#

A page with more than 24 API endpoints keeps 24 of them named instead of 23.

  • The tracker's own overflow row no longer takes a slot. It sums up dozens of calls, so it always won the contest for the last one.
  • Failures of a displaced third-party endpoint move back to the third-party column. They were counted as the site's own.
  • Ad-blocked calls in the nameless other row no longer count as failures. A busy page with blocked tracker calls reports a slightly lower fetch/XHR error rate.
  • Pageviews collected before the deploy keep the shape they were collected in.
ChangedTracker

Readable placeholders in error messages#

  • A stripped literal keeps its quotes. A sample reads '*' is not a valid selector, a removed email reads <email>, and numbers keep the bare *.
  • A URL in a message survives as origin plus path. Query, fragment and numeric path IDs are still removed.
  • Safari selector errors carry their shape again. WebKit's 'x' is not a valid selector arrived unclassified, so the same fault grouped separately per engine.
  • New occurrences classify as selector_invalid, name the selector in message_value and share one fingerprint across engines.
  • Error groups built on message text restart once. Errors with a recognized shape keep their fingerprint; unclassified ones regroup with the tracker rollout.

Docs: Tracker → What a message says.

FixedTracker

Error counts stop at 1000 per pageview#

One visitor stuck in a recursion loop used to report hundreds of thousands of errors.

  • The tracker stops counting at 1000 per channel, errors and failed resources separately. The collector clamps beacons from older builds to the same ceiling.
  • A stored count of exactly 1000 means "1000 or more", and the dashboard writes it as "at least".
  • A breakdown never exceeds the number it breaks down. Where a beacon reports more occurrences than the cap, its signatures are scaled into the stored total.
  • Past the cap the tracker records no signature it has not seen yet. An error that first occurs after a storm of 1000 others can be missing from the list.
  • An error the tracker could not record no longer inflates the count. A revoked Proxy or a throwing toString used to bump the count and lose its entry; it lands as "Other" now.

Docs: Tracker → What counts as an error.

ChangedDashboard

The weekly overview answers what changed this week#

  • Every finding of the week is in one list at the top, grouped under its app, each row leading to the chart behind it. The app blocks below carry the week's numbers.
  • A week in which nothing changed is no longer a good week. A metric in the poor range for two weeks running is named in the opening sentence.
  • Errors are back on the page and in the mail. The error rate is the last column of the table, and a new or spiked error becomes a finding.
  • Every finding says what a visitor notices. Layout shifts mean clicks miss; a slower response to clicks means nothing happens after one.
  • The week is picked in the header bar, where every other page keeps its period.
  • Every heading is the one the mail uses, and the status colours now follow the two colourblind themes.
ChangedDashboard

Every error has its own page#

Opening an error used to expand a row inside a table inside a card, with the stack, the breakdowns and the samples in one table cell.

  • A signature lives at /org/<org>/errors/<fingerprint>, which is also its share link. Existing ?error= links redirect there.
  • The list leads with what broke, in a sentence: what happened, whose code it is, the file, how often it occurred and when it last did.
  • New and rising errors are visible without a click. Each row carries a spark line and a badge, and the list sorts by most frequent, new, rising or last seen.
  • The list ranks by the pageviews an error reached, not by how often it fired. A row whose occurrences come from a handful of visits is marked as a loop.
  • "Where it happens" is the breakdown grid the metric pages carry, bots and network among them. Same filter, exclude and Explorer actions as everywhere else.
  • An error with nothing in it says why: messages or frames switched off, a third-party script without the CORS opt-in, or nothing sent at all. The chart and the breakdown work either way.
  • "AI prompt" hands the whole case to an assistant. It spells out what your site does not collect, and nothing is fetched until you open it.
  • JavaScript errors and failed resources are two tabs, each with its own headline, chart and list.
  • Whose code it is reads as a headline, the breadcrumb names the error instead of its hash, and an expanded row writes its anchor into the URL.

Docs: Analytics → Error Analytics.

ChangedDashboard

One breakdown, wherever you read it#

  • Every dimension is on screen at once, one card each, with a share link that reopens that card. A dimension your site sends nothing for hides its card.
  • A Domain card says which of your domains a slow page or an error belongs to.
  • A card of counted things sorts by its count. Its order is called "Most" and "Fewest" rather than "Worst" and "Best".
  • One rule for colour. Every bar is grey and says how much; the number beside it carries the colour and says how good or bad.
  • Every list says how much of the answer it shows and leads into the Explorer with the page's filters already applied.
  • Opening a row keeps the filters you set, instead of rebuilding them from the row alone.
  • Error breakdowns count errors again. Counts are summed and rates averaged, whatever percentile the page is set to.
  • Countries have flags and browsers have marks in the metric panels too.
ChangedDashboard

The code at the throw site, laid out#

  • The snippet is broken into lines and indented. It breaks at statements and blocks and keeps closers together (}), };, a run of }}}).
  • Nothing is reformatted. The layout inserts whitespace between tokens and changes no character, so the reported column still points at its code.
  • The line with the error is marked with a tint and a rule down its left edge. The snippet wraps instead of scrolling sideways.
  • Snippets grew from 160 to 300 bytes each side of the error column. Cached resolutions are re-fetched once.
  • The AI prompt marks the column inside the code as /*<<COLUMN>>*/ and names it in words above.
  • The error-detail response carries its own diagnosis: error_type, message_kind, message_value, sample_message and messages_collected.
FixedDashboard

The response breakdown lays the backend's phases in sequence#

DB, KV, Search, HTTP and Render each ended at the first byte, so two phases of the same length sat on the same stretch of the timeline and read as parallel work. They now sit inside the backend bar, one after another, in the order a request usually runs. Backend time that no phase accounts for is drawn as "Other app time", and the key-value tier is called "KV" everywhere.

FixedDashboard

Percentages read the way your language writes them#

The German dashboard printed "20.0 %" and "1.49 %" with a point, next to counts that used a comma. Every percentage goes through one formatter now, and so do the abbreviated counts in the breakdown panels.

On a phone a long breadcrumb truncates instead of widening the page, and an error's actions get their own row. A card's controls keep a hairline of their own, and tab strips scroll instead of wrapping.

NewPlatform

IDs in page URLs can be templated away#

  • New per-application setting template_url_ids, off by default. With it on, /order/12345 is stored and reported as /order/:id.
  • It is the rule the fetch/XHR endpoint paths already run: numeric, UUID, long hex and opaque token segments, plus an ID inside a literal segment. Long product slugs stay verbatim.
  • It applies at ingest, to new rows only. A report that spans the switch shows both forms for a while.
  • Turning it off later does not bring the raw URLs back.
  • Checkout-funnel matching and the error-frame strip keep seeing the real path.

Docs: Tracker → template_url_ids.

NewAPI

`lcp_coverage` says how many pageviews reported an LCP#

LCP percentiles skip pageviews without a value, so a country or browser row can rest on a handful of measurements. lcp_coverage is the share of pageviews that reported an LCP at all, 0 to 1: the n next to the p75. It is available in the dashboard API, the MCP server and Ask.

NewTracker

A remote-controlled browser counts as a bot#

The tracker reads navigator.webdriver, which a browser under automation sets itself. The collector stores it as ua_anomaly = webdriver, and bot_class puts it under bot. The hide-bots switch therefore removes Puppeteer, Playwright and Selenium sessions that run without a stealth layer.

ChangedTracker

Errors say what broke, even where no message is kept#

Most sites do not collect error messages, so most errors read as a type and a stack and nothing else. The tracker now recognizes the browser's own wording against a fixed catalogue and sends the shape of it instead of the text.

  • The shape travels in every configuration, Minimal included: a kind from a closed list plus the name that kind points at. An empty JSON response, a request answered with HTML instead of JSON, a variable that does not exist, a selector the browser rejected. An error is readable where it previously showed only a stack.
  • The same fault on Chrome and on Safari is one signature now. The two engines word it differently, and the grouping used to follow the wording.
  • A site that does not collect messages no longer sends them. collect_error_messages is baked into the served bundle, so the message is never assembled. It used to leave the browser and be discarded on arrival.
  • Signatures regroup once. Old groups keep their key and stop growing, the same fault reappears under a new key from the deploy onwards, and occurrence counts from before and after cannot be added up. There is no backfill, because the messages the new grouping reads were never stored.

Docs: Tracker → What a message says.

ChangedDashboard

The error list says what broke, and resolves the stack#

An error row led with the file it was thrown in, which read as "the URL this happened on", and a minified frame like bundle.6a2ce3.js:1:6800 said nothing at all.

  • The row is the message, or the sentence built from its shape where there is none, with type, ownership and throw site under it. The whole stack is in the row too: the throw site at full strength, the callers under it dimmer.
  • "Resolve this stack" reads the file your visitors actually ran. Each frame comes back with the method it sits in, its enclosing module and the code at that column, syntax-colored and scrolled to the failing call. No source map and no setup on your side, because a minifier keeps class and method names. A run of frames in the same vendor bundle folds into one row naming the library, a frame that only passes the error on is marked as a rethrow, and a frame that cannot be read says why. It is a button rather than something a row does by itself, because it fetches your bundle. The endpoint is POST /analytics/error-types/symbolicate.
  • A new chart says which error rose. "Which errors, over time" stacks the five biggest signatures per bucket, named by their message rather than by their hash. Clicking a name in the legend opens that signature below.
  • An error signature is a filter like any other, so LCP, browsers, countries or the funnel can be read over the traffic where one thing breaks.
  • Expanding a signature shows everything we have: occurrences, visitors affected, first and last seen, its shape over time, the pages, browsers, countries, devices and operating systems it hits, and the last pageviews that carried it. The open row is in the address bar.
  • Pages are ranked by how many errors they threw, not by error rate. A page with one pageview that threw once has a rate of 100 % and is not the broken checkout.
  • The same panel sits in the Explorer's row drawer, and a Script error. row now says why it is empty.

Docs: Analytics → What broke, and where.

NewTracker

Page-type detection for themes that renamed their classes#

The built-in rulesets match the classes a shop system ships with, and a custom theme is free to rename them. An OXID theme writing c-body--details where the ruleset expects cl-details recorded no page type at all, and nothing said so.

  • pagetype_rules maps exact class names to page types (home, product, category, cart, checkout, search, account), in the page-type setting and on POST/PATCH /applications. The rules sit in front of the ruleset instead of replacing it, so you fix the one page type your theme renamed and the other six keep coming from the ruleset. Rules without a ruleset are a mode of their own: a shop system or CMS we have no ruleset for gets a page type as soon as you name its classes.
  • Class names are exact, never patterns. The comparison runs in every visitor's browser, and a pattern there would be a hazard on your own page. At most 24 rules, at most 32 characters per class, and case does not matter, unlike in the built-in rulesets.
  • Detect names your shop system, so switching detection on no longer starts with reading your own body class in DevTools. It reads the home page of the application's oldest domain and says what it found, with the evidence and the page type it would have recorded. Shopify and PrestaShop are recognized and flagged as not selectable: Shopify reports its page type through Server-Timing instead, PrestaShop has no ruleset yet.
  • Discover works out the class names. Name one example page per page type and your home page is read alongside as the contrast, so the class that only that page type carries can be proposed. Two examples of one type give the surest answer, because whatever differs between them cannot be the marker. A type with exactly one candidate comes back ready to save.
  • Neither probe writes anything, both read only the application's own domains, and both are limited to ten runs per organization per hour.
  • The read is unchanged: one token per pageview, only where you named the class or a ruleset knows it, and never the full class attribute. OXID shops also report the order-completed page (cl-thankyou) as checkout now.

Docs: Tracker → pagetype_rules.

ChangedAPI

A time series with a dimension keeps its whole time axis#

A query that groups by time and by a dimension used to lose the end of its axis, because the row limit cut the result before the window was covered. Six hours by minute over twenty error signatures drew the first two hours and looked complete.

  • group_by.top_n limits how many dimension values a query reads, ranked by pageview volume over the requested window. Set it on every query that groups by time and by a dimension, and the row limit stops competing with the time axis.
  • Grouping /analytics/query by an errors.* dimension works again. The row count that paginates the result asked ClickHouse for a column the query had not selected, so every such query failed instead of returning data.
  • A grouped query reports the dimension value it grouped by. Both structured dimensions were read under the wrong name: errors.* came back empty, and a map dimension such as third_parties failed the request.

Docs: API → Analytics.

FixedDashboard

Analytics fits on a phone#

The Active hours card was as wide as the week it draws, so on a phone it pushed the hour axis and the Pageviews / Visitors switch out of the viewport with nothing to scroll. The grid scrolls inside the card now. Tapping the info icon on a KPI tile no longer switches the chart's metric, and the error list keeps its count and share on a 390 px screen instead of cutting them off.

FixedDashboard

Signing in lands on the dashboard#

Opening the app root while signed in bounced you to the login page. / was still on the list of public marketing pages, from back when it served the landing page, so the session was never checked before the route guard ran, and only a reload got you to the dashboard.

FixedDashboard

A short Explorer range shows times on its axis#

Every window that buckets by minute, which is 15 minutes through 6 hours, drew the same date on every tick and no time at all. The chart now uses the app's shared tick grid: steps aligned to local midnight, with the date on the tick that lands on midnight.

NewAPI

Failed resources can be drilled into and filtered#

The failed-resource list could rank hosts and paths and nothing else, and one tracker that every ad blocker kills owned the top rows.

  • The list groups by host, each with its share of all failed loads, and opening a host shows the individual resources under it. A second view ranks pages by the share of their pageviews that lost a resource.
  • Hiding a host makes the list readable. host and exclude_hosts narrow the totals as well, unlike bucket, so the share bars and the big number keep agreeing with the rows.
  • Opening a resource drills into it the way opening an error signature does: the file, a time series, breakdowns over page, browser, device, OS and country, visitors affected, and sample beacons. POST /analytics/failed-resources/detail, keyed by the fingerprint each row now carries.
  • The beacon drill-down tells the two populations apart. errors carries JavaScript errors, failed_resource_loads carries the failed loads with host, path and owner, and failed_resources is a column you can project and scope with >= 1. error_fingerprint takes an error_fingerprint_kind (error or failed_resource), because both lists hand out keys from one space.
  • The assistant and the MCP server gained get_failed_resource_detail, the counterpart of get_error_detail for the other population.

Docs: Analytics → Failed resource loads.

NewDashboard

Notices from fastmon appear in the dashboard#

Messages written by fastmon staff now show as a strip at the top of the dashboard, in the same place and the same shape as the billing "action required" strip, so the two read as one system.

  • A notice is addressed to everyone or to one organization, reads as info, warning or critical, and only shows while it is published and inside its own time window. An organization's notices are visible to everyone who may open it, agency staff working on it through a partnership included.
  • A title on its own is a complete notice. The description is optional, and line breaks in it are kept as typed.
  • Clicking one away is remembered per person on the server, so it stays gone on every device and in every browser.
  • Several at once page one at a time with an arrow and a count ("2/3") instead of stacking. The most severe one is shown first, and nothing moves on its own.
  • German where it was written. A notice can carry a German title and description; everyone whose account language is German reads that version, everyone else reads the base text.
ChangedDashboard

First-party and third-party are written out#

Everywhere the dashboard splits by whose host something was on, it writes the word. English says "First-party" and "Third-party", German says "Eigene" and "Dritte". That covers the fetch/XHR panel, the error list, the error-origin bars, the failed-resource block, the Explorer's error-origin dimension and the Lighthouse resource table.

  • Where a row is too cramped for a word, only the third-party side is marked, and an unmarked row is your own. The fetch/XHR waterfall and the script lists on the INP and TBT pages read a lot quieter for it.
  • Same-origin calls in the fetch/XHR panel no longer show up as "1P" in front of a path. A path without a host is your own domain, the way a URL reads.
  • Unchanged: "First-party proxy" and the collector party badge in the setup. There the word names a configuration mode, not who a host belongs to.
ChangedDashboard

The Pages panel's third tab lists domains#

It grouped by the serving node from the optional fm-host Server-Timing key, so on every site that does not send that header the tab was a single "(not reported)" row. It lists the domains out of the page URLs now, and is called Domains. The serving node is still in the Explorer and on the Backend time page, where it answers a performance question.

ChangedTracker

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 at POST /analytics/failed-resources. Each row says whose host it was, yours or a foreign one. The MCP server gained get_failed_resources. The error-types endpoint now refuses error_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, errc and frc, 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.example had 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.

NewTracker

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 sends Access-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 ScriptError rows are present, POST /analytics/error-types also lists own_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.

Docs: Tracker → What counts as an error.

NewPlatform

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, with q (substring, case-insensitive), kinds to narrow, and limit (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 without site:read gets no sites whatever its owner's role is.

Docs: API → Search, Analytics → Navigating with the command palette.

NewDashboard

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.

ChangedDashboard

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".
FixedDashboard

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.

NewPlatform

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. plan starts at none and 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 are none, beta, light, standard, partner and enterprise; pro is now standard and starter is now light.
  • 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:read opens 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:manage moves 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.

Docs: Organizations → Plans, Errors → Entitlement.

NewAPI

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=42 fills cdn_age, fm-origin-age;desc=312 fills origin_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 rides desc, not dur: dur is 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, origin and content (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-fpc is renamed to fm-origin-cache and the old name is no longer recognized. It was the only first-party key calling the origin tier something other than origin, which next to fm-origin-age would have put two words for one tier in a single header. A header still sending fm-fpc shows it in the panel's top-keys list instead of silently filling nothing. fm-cdn keeps working; fm-cdn-cache is the documented spelling.

Docs: Server-Timing → Cache age.

RemovedAPI

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-keys entries no longer carry oauth_client_id. Connected apps were never meant to be read from that list; they are at /account/connections and under the organization's connections.
  • Every key now audits as api_key.issued and api_key.revoked. The app_token.* entries belong to grants alone, which is what they described on the reading side all along.

Docs: API → Authentication.

ChangedPlatform

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_overview gained a samples block, compare_periods and detect_changes one 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, since inp_p75_ms: null beside samples.inp: 0 is "nothing measured here" rather than a disabled feature.
  • A url filter 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"] and utm_source: "Google" each answered zero on data holding exactly what was asked for, because the stored spelling is Chrome but desktop. country refuses a name outright and repairs the codes ISO does not assign (UK and EN to GB, EL to GR).
  • get_breakdown reports the floor it applied and stops flooring counters. min_samples dropped rows invisibly, and an empty result under a floor of 50 is a different finding from an empty dimension. It also hit pageviews, where the value is the sample count, so "top pages by traffic" on a small site returned one row and dropped nine.
  • get_error_detail stops inventing a signature. An aggregate over no rows returned type Error with 1970-01-01 timestamps; responses now carry found, 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.

Docs: MCP server → What the model can read.

NewPlatform

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:read and 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.

ChangedAPI

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/authorize takes scope. 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 as invalid_scope.
  • A registration that names no scope declares analytics:read, instead of the full permission catalog. Existing registrations keep what they have. Read the scope in the response to see what you got.
  • A registration that names no credential_owner gets a personal connection, which used to be an organization-owned one. Declare organization explicitly 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.

ChangedDashboard

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.
FixedDashboard

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.

NewDashboard

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.

Docs: Server-Timing → Origin host and logged-in.

August 2026

NewAPI

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-03 becomes origin_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=no becomes logged_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 onto yes/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 in fm-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.

Docs: Server-Timing → Origin host and logged-in.

NewAPI

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 new org_key:manage permission (members and owners, not viewers, never partner access). The secret starts with fmo_ 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:manage can 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.

ChangedPlatform

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.

ChangedAPI

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.

ChangedAPI

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.

NewPlatform

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 role and permissions; GET /v1/permissions lists 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_denied with the missing permission in details.permission replaces owner_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.

ChangedAPI

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/self shows what a key can do per organization.
  • Expiry and last use: an expired key answers 401 credential_expired; last_used_at says 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 /admin routes 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.

NewDashboard

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.

Docs: Compare releases → Create an API key.

ChangedAPI

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_dur now sums redis, valkey, kv, fm-kv, memcache, and cache; the dashboard labels it "KV / Cache". apcu leaves the sums (drill-down only).
  • http_dur replaces external_dur with the same members and the alias fm-http. fm-cache and fm-external are dropped; no site ever sent them.
  • mongodb and sqlite add into db_dur on top of the relational alias: where a document store appears, it is the primary datastore.
  • count per phase on POST /analytics/server-timing: the number of pageviews behind each percentile.
  • queue and twig join the catalog (no custom slot used). Stored values are not rewritten; notification rules naming the old metrics keep evaluating on the new ones.

Docs: Server-Timing → Backend phases.

ChangedDashboard

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.

NewDashboard

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.

ChangedDashboard

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.
ChangedDashboard

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.

ChangedDashboard

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.

NewAPI

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_dur sums elasticsearch, opensearch, solr, search, and fm-search.
  • kv_dur sums redis, valkey, kv, and fm-kv.
  • Both are percentile phases on POST /analytics/server-timing, row fields on /analytics/beacons, and aggregatable metrics. db_dur stays purely relational.
  • cache_dur changes meaning: redis moves to kv_dur, edge_cart leaves the sums. Stored values are not rewritten, so a long-window cache_dur chart steps down at the deploy. If Redis is your cache, emit fm-cache on top.
  • Shopify hygiene: compressionlevel and speculationtags are dropped (configuration, not timing), compressiontime joins the catalog, and a numeric desc outside the duration window (a theme id) stays a description instead of becoming a six-year duration.

Docs: Server-Timing → Backend phases.

NewTracker

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 key frs, Chromium 115 and later, null elsewhere), with TTFB's activation adjustment and 60-second cap. The collector stores it as ttfb_final and drops values below ttfb.
  • Reading the pair: empty means the browser does not report it, or a soft navigation or bfcache restore; equal to ttfb means no interim response; above ttfb means Early Hints, and the difference is the edge's head start.
  • TTFB stays responseStart, so it keeps comparing to CrUX. ttfb_final is 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 compressionlevel and speculationtags, mirroring the collector.

Docs: TTFB → Early Hints.

ChangedAPI

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.

NewDashboard

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_stage can 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_stage as group-by and exclude, cart_interactions and cart_interaction_errors as metrics and metric ranges, all on /analytics/beacons rows too.

Docs: Checkout funnel → Where it shows up.

FixedDashboard

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.

NewDashboard

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.

ChangedDashboard

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.

ChangedDashboard

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.

NewDashboard

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 POST to 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_stage as 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 need collect_fetch_xhr.
  • API: POST /analytics/funnels/checkout and /journeys; contains_checkout_stage and contains_cart_interaction on the visitors list.

Docs: Checkout funnel.

NewDashboard

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.
NewDashboard

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.
NewDashboard

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.
NewPlatform

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-Key header on the beacon route, and the edge reads the real visitor IP from your proxy's X-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: POST and DELETE /v1/applications/{id}/proxy-secret.

Docs: First-party proxy guide, with nginx, Caddy, and Cloudflare Worker snippets.

FixedTracker

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 expected is-ctl-index class 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-* and is-act-* classes. The tracker prefers the is-active-route-* class shops emit since about 6.6. On it the checkout splits properly: the cart page as cart, the confirm, finish, and register steps as checkout, the account area as account. Older shops fall back to the legacy classes, where cart and checkout share cart.
  • A browser-cached tracker keeps its old behavior until the cache rolls over.

Docs: Tracker → ruleset table.

ChangedDashboard

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.
NewDashboard

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/beacons accepts order_by as {field, direction} over any scalar row field.
NewDashboard

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/beacons gains a server_timing filter for a key or one of its values.
  • PoP keys are kept: pop, fastly-pop, cdn-pop, and x-qc-pop name the CDN node, not the visitor (asn, country, and ipv6 stay blocked).
  • Fix: expanding the trend of a custom key with a hyphen or dot in its name (wp-total) returned an error.
NewDashboard

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.

FixedDashboard

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.

ChangedDashboard

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.
ChangedDashboard

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

FixedPlatform

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.

NewDashboard

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.

Docs: Privacy → Bot and datacenter labels.

ChangedDashboard

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).

ChangedDashboard

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.

ChangedTracker

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, the utm_* and gad_* families), on endpoint paths and page URLs, even with collect_query_keys on. Naming one in collect_query_values_for keeps it.

Docs: Tracker → collect_query_keys.

ChangedPlatform

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.

ChangedPlatform

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.

ChangedDashboard

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.

ChangedDashboard

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_session is collect_sessions, preconsent_level is session_consent (deferred or immediate), and the error, query, and fetch/XHR switches gain a collect_ prefix.

Docs: Applications, Sites, Tracker settings.

NewDashboard

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.

FixedDashboard

'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.

FixedPlatform

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.

NewExtension

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 init to the terminal beacon, and shows the Web Vitals as they settle.
  • Captures the fetch beacons and the sendBeacon bodies 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.

NewPlatform

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.

NewDashboard

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.

ChangedTracker

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.

  • init fires immediately as a pageview-exists marker, so a bot or an abruptly closed page still registers.
  • loaded waits 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.

FixedTracker

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.

FixedTracker

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.

NewDashboard

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.

NewDashboard

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.

ChangedTracker

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.

FixedTracker

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.

ChangedDashboard

'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.

NewDashboard

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.

ChangedDashboard

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.

NewTracker

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_for keeps the chosen key=value pairs, collect_query_keys the 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.

Docs: Tracker → collect_query_values_for.

NewAPI

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).

NewAPI

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.

ChangedTracker

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.
NewTracker

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-98123 are 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.
NewDashboard

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.
NewAPI

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.

ChangedDashboard

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.

NewDashboard

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.

NewDashboard

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.

NewDashboard

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.

NewDashboard

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.

NewDashboard

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.

NewTracker

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.

NewTracker

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.

ChangedTracker

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.

NewDashboard

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.

NewDashboard

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.

ChangedDashboard

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.

ChangedDashboard

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.

ChangedDashboard

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.

FixedDashboard

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.

ChangedTracker

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.

ChangedPlatform

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.

ChangedDashboard

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.

ChangedDashboard

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.

FixedPlatform

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.

ChangedDashboard

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.

ChangedDashboard

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.

NewDashboard

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.

FixedDashboard

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.

ChangedDashboard

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.
ChangedDashboard

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

NewDashboard

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.

ChangedDashboard

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.

NewDashboard

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.

FixedDashboard

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%”.

NewDashboard

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.

ChangedDashboard

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.
ChangedAPI

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.

ChangedDashboard

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.

NewDashboard

“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.

NewDashboard

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.

FixedDashboard

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.

ChangedDashboard

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.

ChangedTracker

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.

NewPlatform

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.

NewDashboard

Cache-hit visibility#

New CDN Cache and Origin Cache (HIT/MISS) columns and a cache-status filter in the beacon explorer.

FixedPlatform

AI “Ask” error drill-down#

The AI Ask error drill-down returns its summary and time-series correctly instead of erroring on every call.

NewPlatform

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.

NewPlatform

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.

ChangedDashboard

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.)

FixedDashboard

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.

FixedDashboard

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).

NewAPI

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.

ChangedAPI

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.

ChangedDashboard

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.

FixedDashboard

Visitors-list filter crash#

Filtering the visitors list by device type, browser, country, or OS no longer errors.

ChangedDashboard

“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.

NewPlatform

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.

NewDashboard

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.

NewDashboard

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).

NewDashboard

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.

RemovedDashboard

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

ChangedPlatform

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.

NewDashboard

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.

ChangedPlatform

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.

NewDashboard

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).

ChangedDashboard

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.

RemovedTracker

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.

ChangedTracker

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.