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/:idyesterday 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. UsemarkResponsefor 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.