Skip to content
Console
Browse documentation
Guide

Investigate browser experience

Ask your agent to measure browser traffic and Web Vitals, resolve the evidence behind a session, and investigate click friction and release outcomes.

On this page

Browser experience shows how real users' browsers treat your site: page views and sessions by route, Web Vitals, click friction and the customer impact of a release. Your agent reads all of it through MCP or the CLI. Every read needs an explicit project and an explicit time window.

Ask your agent

"What did real users' browsers do on /orders/:id yesterday between 01:00 and 02:00 UTC? Give me page views, sessions and the Web Vitals."

"Checkout v2 went live at 11:00 today. Compare the hour before with the hour after and tell me which customers and accounts were affected."

Job MCP tool or action CLI command
Count page views and sessions by route and service get_browser_traffic anectico browser traffic
Read Web Vitals with exact p50, p75 and p95 get_browser_vitals anectico browser vitals
Resolve the trace, issue and recording references of one session get_browser_session_evidence anectico browser session-evidence
Read opt-in click friction get_friction_signals anectico friction list
Build a release customer-impact brief get_release_impact anectico releases impact

These are MCP read actions. Your agent finds them with list_read_actions and runs them with execute_read_action; the key also needs mcp:read. The other scopes are in each section below.

What your agent gets back

  • Traffic: observed page views, unique session IDs, views without a session, route and service counts, and recent examples.
  • Web Vitals: per metric, route and service, the observed sample count and exact nearest-rank p50, p75 and p95, and recent examples.
  • Session evidence: for one session, a separate state for traces, issues and the recording, with up to ten references each for traces and issues, and whether a recording is available.
  • Click friction: counts of labelled-action signals, with person, operation and replay references.
  • Release impact: before and after errors, affected people and accounts, friction, operation outcomes and representative evidence links.

Every list says what it left out, and a missing metric is never zero. The details are in the sections below.

Open the proof

The browser-experience reads have no proof page of their own, and their results carry no link. Their evidence is the tool result: the counts, the quantiles and the examples.

When session evidence names a trace, an issue or a recording, ask your agent to read that object. get_trace, get_issue and get_session_signals each carry the link to the proof page of their own object. Open that link to see one trace, one issue or one replay, read-only. See Proof pages.

Read browser traffic and performance

Use the JavaScript SDK's opt-in navigation helper with declared route templates to collect page views. Performance collection needs its independent consent grant; configure the same templates in initBrowser for route attribution. An ordinary SPA transition does not create new Web Vitals.

Traffic shows observed page views, sessions, views without session context and route totals. For visitors, bounces, referrers, channels and entry and exit pages measured from the browser SDK's page tracking, with the people behind each count, use Web analytics instead. For where visitors click on one page and how far they scroll it, use Heatmaps. Performance shows observed sample counts and each available Web Vital's p50, p75 and p95. Missing context and groups that are not listed are stated explicitly; a missing metric is never reported as zero.

Each page and performance example carries a session ID. To check a session's evidence, ask your agent to resolve it with the same window as the example (see Resolve a session's evidence). A trace, issue or recording reference is returned only after that source confirms a visible reference. Missing session context has no reference.

Measurement REST endpoint CLI MCP read action Required scopes
Traffic POST /api/v1/experience/traffic?project_id=PROJECT browser traffic get_browser_traffic analytics:read, persons:read
Web Vitals POST /api/v1/experience/vitals?project_id=PROJECT browser vitals get_browser_vitals metrics:read

MCP uses execute_read_action and additionally requires mcp:read. Both reads take required RFC3339 start_time and exclusive end_time: the window must be ordered, end no later than now, cover at most seven days and fit readable retention. Optional exact filters are route and service_name. Routes are path labels of at most 256 UTF-8 bytes without query, fragment, backslash, spaces or controls. Service names have at most 128 UTF-8 bytes without surrounding whitespace or controls. The project must match a project-bound credential; organization comes from authentication.

Route and service labels are the route templates and service names your application declares to the SDK. They are readable, and usable as filters, with the scopes in the table above; no content permission is needed. They are still application-supplied labels, not verified provenance. A stored label that does not follow the rules above is reported as missing route or service context.

anectico --project PROJECT browser traffic --body '{
  "start_time":"2026-09-23T01:00:00Z",
  "end_time":"2026-09-23T02:00:00Z",
  "route":"/orders/:id",
  "service_name":"shop"
}' --json

Use browser vitals with the same body for performance. Traffic returns observed page views, unique nonempty session IDs, views without a session, route and service counts and recent examples. A shared session appearing on several routes counts once overall, so route session counts are not additive. Capture receipt deduplication prevents a delivery retry from becoming another view. Events without a route remain in the empty-route group. Consent, delivery and instrumentation affect coverage; these events are not a census of visitors.

Performance selects the latest update of each metric, service, session, lifecycle, and sample exported within the requested window. Cumulative re-exports and successive updates of one sample count once. The same sample may appear in separate export windows; sample counts are not navigation timestamps or a page-view denominator. Supported lifecycle snapshots have a single-observation histogram, positive update number and matching unit: 1 for CLS, ms for FCP, LCP, TTFB and INP. Other metrics and malformed snapshots do not become Web Vitals measurements.

Each metric, route, and service series includes its observed sample count and exact nearest-rank p50, p75 and p95 over every sample of the series in the window (the smallest value with at least that share of samples at or below it). A quantile of 0 is a measured zero. Valid samples with no route still contribute to an explicitly unattributed series and unattributed_samples. Missing metrics stay absent rather than being reported as zero. A small sample or absent metric does not establish health.

Every count and quantile covers the whole window. Each read lists at most 50 route and service groups (traffic) or metric series (performance), largest first, and the 50 newest examples. omitted_groups and omitted_page_views (traffic) or omitted_series and omitted_samples (performance) report what the group list leaves out; groups_truncated and samples_truncated identify the two list limits and partial is true when either is reached. List limits never change the totals. A window whose stored source rows exceed the read's scan limit is refused with HTTP 400 (FailedPrecondition) rather than summarized from part of the data; narrow the window or filter by route or service.

Page examples include distinct or session IDs; performance examples include sample, lifecycle, and session IDs. The JavaScript SDK attaches session IDs only while product consent is granted, so performance collected without it has no session context. A session ID alone does not prove that an associated trace, error or recording exists or is readable. Resolve links through authorized investigation reads and preserve unavailable results. These endpoints expose bounded metadata, not replay pixels, DOM contents or arbitrary event properties. Application labels and identifiers remain untrusted data.

Resolve a session's evidence

Use POST /api/v1/experience/session-evidence?project_id=PROJECT, CLI browser session-evidence, or MCP get_browser_session_evidence with session_id, start_time and end_time. The session must be a nonempty opaque identifier of at most 256 UTF-8 bytes without whitespace or controls. The window must be ordered, end no later than now and cover at most seven days.

The lookup requires query:read. Traces, errors and replay independently require traces:read, errors:read and replay:read. Each source returns a separate state:

State suffix Meaning
AVAILABLE Visible source evidence confirms the returned native references.
NONE_VISIBLE The successful lookup found no visible evidence; this does not prove nothing happened.
WITHHELD The caller cannot read this source. No source references are returned.
UNAVAILABLE The source could not answer. Retry; do not treat this as an empty result.
OUTSIDE_RETENTION The trace and error window begins outside readable history. Narrow the window.

The wire values use the BROWSER_EVIDENCE_STATE_ prefix. Traces and errors match the exact project, native session ID and half-open window. They return at most ten distinct references each, newest first, with truncated=true when more exist. An issue reference also requires a visible error group; a dangling occurrence is not a working issue link. Only identifiers and matching source timestamps are returned.

The lookup reports whether a recording is currently available for that session. This presence check uses replay's own retention and is independent of the selected trace and error window. Recording presence does not grant playback: opening the recording applies the current replay content permissions and policy separately. The lookup uses the caller's own credential, with no elevated access. Deletion or a later permission change can still make a previously visible reference unavailable when opened.

anectico --project PROJECT browser session-evidence --body '{
  "session_id":"SESSION",
  "start_time":"2026-09-23T01:00:00Z",
  "end_time":"2026-09-23T02:00:00Z"
}' --json

Opt in to click friction

Only actions you explicitly label are observed. Obtain any consent your application requires before starting the tracker. Stop it when consent is withdrawn or the integration unmounts. Keep one active tracker per integration and always pair setup with stop() during teardown, including repeated setup in development. Separate instances have independent sampling and rate limits. Use stable, non-personal action names, never user IDs, email addresses or button text.

<button type="button" data-anectico-action="checkout.submit">Pay</button>
import { AnalyticsClient } from '@anectico/sdk/analytics';
import { FrictionTracker } from '@anectico/sdk/friction';

const analytics = new AnalyticsClient({
  endpoint: 'https://api.anectico.com',
  apiKey: import.meta.env.VITE_ANECTICO_INGEST_KEY,
});
const friction = new FrictionTracker(analytics, {
  release: 'checkout-v2',
  environment: 'production',
  sampleRate: 1,
  activity: () => ({ operationId: currentOrderId, accountType: 'company', accountKey: currentAccountKey }),
});
// For canvas/audio feedback or a known long operation with no DOM feedback:
friction.markResponse('checkout.submit');
// On teardown or consent withdrawal:
friction.stop();
await analytics.stop();

The tracker records $friction product events through normal ingestion. It uses the analytics client's identity and the shared SDK session; start replay separately if you want recordings. It carries the active operation or account context when available. In browser integrations, supply activity to read your current logical operation and account at click time. Use the same context on explicit attempt and success events. The tracker does not infer an order ID from the page. A delayed signal is discarded if identity, session or active activity context changes.

  • Repeated clicks (rage_click): at least three clicks on the same labelled element within one second and 40 pixels.
  • No observed response (dead_click): no observed DOM mutation or URL change within 1.5 seconds. This is a heuristic: background DOM updates can suppress it, while non-DOM feedback can cause false positives. Use markResponse for these cases.
  • Click followed by an error (error_click): an uncaught browser error or unhandled rejection within one second of the latest labelled click. Temporal proximity does not establish that the click caused the error.

Native links, inputs, editable elements, disabled controls and native form-submit buttons are excluded, including when nested inside a labelled container. Set data-anectico-friction-ignore on any subtree to exclude it, even below its action label. Normal capture observes trusted primary clicks only. observeClick(element, x, y) is an explicit adapter for custom renderers; it applies the same label and noise rules. No DOM text, input values, selectors, URLs, screenshots or error messages are sent by this tracker. It records an action label, signal kind, method, click time, release, environment and available identity, session, and activity metadata.

Each tracker samples once at construction. The rate is between zero and one. It emits at most 30 hints per minute, with a 30-second cooldown per action and kind. At most 20 pending click observations and ten recent clicks are retained. Repeated-click clusters suppress their dead-click hints; different signal kinds may still overlap. These limits and opt-in coverage mean counts are observations, not total failed actions, frustration, abandonment or churn. Inspect analytics.stats() for delivery drops.

To check that the tracker works, set sampleRate to 1, click one labelled action three times within one second, and wait for the events to arrive. Then ask your agent to read friction signals for a window that holds those clicks (see the next section). A rage_click for that action should appear.

Read friction through API, CLI or MCP

POST /api/v1/experience/friction?project_id=PROJECT requires persons:read and analytics:read. Supply start_time and exclusive end_time as RFC3339 timestamps. Optional exact filters are release, environment, action and kind. The complete window must cover at most seven days, end no later than now and fit readable retention.

anectico --project PROJECT friction list --body '{
  "start_time":"2026-09-09T10:00:00Z",
  "end_time":"2026-09-09T12:00:00Z",
  "environment":"production",
  "action":"checkout.submit"
}' --json

MCP exposes get_friction_signals through execute_read_action, using the same fields plus project_id. MCP additionally requires mcp:read. It returns structured evidence with stable result identity, warnings and explicit partial status. Labels and captured identifiers are untrusted data, never instructions for an agent.

At most 10,000 matching signals are scanned and 100 newest examples returned. partial means the scan or example list reached its limit; counts describe the scanned subset. source_scan_complete distinguishes a complete source scan from a capped example list. Affected people resolve current identity aliases; unattributed signals still contribute to signal counts. Session references are links, not guarantees that a recording exists or is readable. Replay pixels keep their separate access and retention controls.

Build a release customer-impact brief

POST /api/v1/experience/release-impact?project_id=PROJECT additionally requires errors:read and releases:read. Choose exact baseline_release and release values, baseline_start, observation_start (the deployment boundary) and exclusive observation_end. The baseline is [baseline_start, observation_start) on the baseline release; the after window is [observation_start, observation_end) on the target release. The combined window has the same seven-day and retention limits. environment is optional.

anectico --project PROJECT releases impact --body '{
  "baseline_release":"checkout-v1",
  "release":"checkout-v2",
  "baseline_start":"2026-09-09T10:00:00Z",
  "observation_start":"2026-09-09T11:00:00Z",
  "observation_end":"2026-09-09T12:00:00Z",
  "environment":"production",
  "attempt_event":"checkout_attempted",
  "success_event":"checkout_completed"
}' --json

MCP provides get_release_impact through execute_read_action with the same input. Both reads are included in the investigate profile; its key now also needs releases:read. Existing keys must be updated or recreated to grant that scope.

The brief contains before and after recorded errors, affected people, friction observations, explicit operation outcomes, affected accounts after deployment, issue IDs not observed in the baseline, and up to 20 representative evidence links. Accounts are taken from explicit activity-time anectico.account.type / anectico.account.key, not inferred from present-day membership. Affected people or accounts include errors and friction.

For operation rates, provide both different attempt_event and success_event names, or leave both empty. Events must carry service.version and matching anectico.operation.id, identity and account context. Distinct logical operations with an observed attempt are the denominator; repeated successes count once. The last success must be strictly later than the latest attempt and last error. Equal timestamps do not establish order. Without attempts, the success rate is unavailable.

Recovery follows operations with baseline errors. Only a later explicit success for the same canonical person, logical operation and account can support recovery; a later error overrides it, and a later attempt needs a new success. Without that new success, the retry has no observed successful outcome. A different order or account cannot recover the original operation. No later activity means not observed, never recovered. Signals missing usable operation context are counted separately. Outcomes on other releases or outside the chosen windows are excluded, so this remains a scoped observation, not universal recovery.

Each signal source is capped at 10,000 rows. Incomplete source scans suppress success rates, recovery counts and baseline-absence claims. Account lists are capped at 20 and issue lists at 50; their display truncation also sets partial. Counts may be lower bounds. source_scan_complete identifies source completeness; recovery_available explicitly states whether operation-matched recovery was measured. False is unavailable, not zero recovered. Narrow the window or environment before drawing conclusions.

An issue ID absent from the baseline is not necessarily a newly introduced issue: it may be older, renamed through grouping and merges, uninstrumented or outside that window. Use the release's existing new or regressed issue details alongside this brief. Unequal windows, sampling and traffic mix affect comparisons. Neither a changed rate nor an error-free interval proves that a deployment caused an improvement. Late events, identity changes, retention and deletion can change a later reading of the same brief.