Tools
Every tool the MCP server offers, its parameters and the permission it needs.
Every tool runs one route of the REST API with the caller's credential: one authorization path, so a
tool needs the same permission as its route, checks site access the same way and answers the same data. The
tool reference below lists every tool with its arguments, its permission and the route it runs. It
is generated from the server's own tools/list answer, and the backend's tests fail when it falls behind.
A client sees only the tools its credential allows: an API key gets the tools of its
permissions, an OAuth token those of its scopes and the person's role together. Calling a tool
the credential does not have is a JSON-RPC error, -32602 "Tool create_goal not found".
How a tool runs
The tool's arguments become the route's request:
- Arguments named in the route's path (
siteId, andid,sessionIdoruserIdwhere the route has them) fill the path. An empty value,.or..is refused before the route runs (bad_request, "Invalid id" for anid). body, for the tools that have it, is the route's JSON request body.- Every other argument goes to the query string, to the REST parameter of the same name;
filtersis sent as JSON, as the REST API expects. The route's page in the API reference documents the values each parameter takes (GET /api/v1/sites/{siteId}/pagesfor thepagestool, for example). list_sitestakes no organization: it reads the credential's own organization.
The route then validates the request, checks the credential's access to the site and runs. When the client cancels
an MCP request, a GET tool's route stops its query too.
Arguments shared by the reports
Site
siteId is the site's public id, the same value as the tracking script's data-site attribute. list_sites returns
the ids the credential can reach.
Time window
Every tool that takes start, end, last and all needs a time window, given in exactly one of three ways:
| Arguments | Window |
|---|---|
last | The last 30m, 12h, 7d…, up to 400d. |
start and end | Two dates (YYYY-MM-DD, in the time zone tz, end included), or two ISO instants with an offset (end excluded). Both in the same form. |
all | true: all time. |
The tool's input schema (and the reference below) marks each of these arguments as optional,
because the REST route checks the combination, not the schema; the description of start says that every call needs
exactly one window. A call without a window, or with two forms, fails with this error result:
{"code":"bad_request","message":"Invalid request","details":[{"in":"querystring","path":"/","code":"custom","message":"exactly one of start+end, last or all is required"}]}This holds for overview, timeseries, breakdown, pages, geo_countries, geo_points, events,
event_names, errors, vitals, sessions, users, analyze_funnel, journeys, retention, revenue_summary,
revenue_breakdown and annotations. The one exception is events with since (a realtime tail of the events since
an ISO instant within the last 24 hours), which takes no window. The other tools (list_sites, get_site, live,
session, user, goals, funnels, segments, and the create, update, save and delete tools) have no window
arguments. If you write instructions for an assistant, tell it to pass a window, such as "last": "28d".
tz is an IANA time zone (default: the site's). bucket sets the time series' step (default: chosen from the
window's length). compare (previous or year) adds the previous period of the same length, or the same period a
year earlier, to overview, timeseries and revenue_summary, the only tools that take it. See
Time windows, filters and comparisons for the details.
Filters and saved segments
filters is a list of conditions, all of which must hold (at most 20): { "dim": "country", "op": "eq", "values": ["US"] }. The dimensions and operators are the REST API's, listed in
Time windows, filters and comparisons.
segment applies a saved segment: pass the id of one returned by the segments tool, and its filters apply,
merged with any filters you add. These two calls give the same numbers when segment ae378024-… holds those two
filters:
{ "siteId": "SITE_ID", "last": "7d", "segment": "ae378024-68b3-43b3-8ba3-5860a3a5beec" }{
"siteId": "SITE_ID",
"last": "7d",
"filters": [
{ "dim": "device_type", "op": "eq", "values": ["desktop"] },
{ "dim": "referrer_domain", "op": "eq", "values": ["google.com"] }
]
}An API key sees only the segments shared with the organization; an OAuth token also sees the person's own private
segments. A segment the credential cannot see answers not_found "Segment not found".
Paging
Lists return one page at a time. breakdown, pages, errors and revenue_breakdown take limit and offset,
and their meta.totalCount gives the number of rows. events, sessions and users take limit and cursor:
pass the previous page's meta.nextCursor as cursor to get the next one (it is null on the last page). See
Pagination.
Results and error results
A successful call returns one text block holding the route's JSON response, { "data": …, "meta": … }, with every
string cleaned of invisible and control characters (see
Tool results are data, not instructions). A tool whose route
answers 204 No Content, such as delete_goal, returns {}.
{
"content": [
{
"type": "text",
"text": "{\"data\":[{\"value\":\"google.com\",\"visitors\":72,\"sessions\":78,\"pageviews\":177,\"bounce_rate\":35.8974358974359,\"avg_session_duration\":90.77826923076924,\"percentage\":36.18090452261307},{\"value\":\"news.ycombinator.com\",\"visitors\":22,\"sessions\":23,\"pageviews\":49,\"bounce_rate\":47.82608695652174,\"avg_session_duration\":87.45765217391305,\"percentage\":11.055276381909549}],\"meta\":{\"window\":{\"from\":\"2026-10-20T20:39:21.096Z\",\"to\":\"2026-10-27T20:39:21.096Z\",\"tz\":\"UTC\",\"bucket\":\"day\"},\"totalCount\":7,\"cache\":\"miss\"}}"
}
]
}A call that fails returns isError: true and a text block:
| Failure | Text |
|---|---|
| The route refused the request | The REST error object, e.g. {"code":"not_found","message":"Site not found"} or {"code":"bad_request","message":"Invalid request","details":[{"in":"querystring","path":"/last","code":"custom","message":"last must look like 30m, 12h or 7d"}]} |
| The arguments do not match the tool's input schema | Input validation error: Invalid arguments for tool overview: siteId: Invalid input: expected string, received undefined |
| The MCP request was cancelled | {"code":"aborted"} |
A site the credential cannot reach answers not_found, whether it exists or not.
Write tools
The tools whose Effect is "adds" or "changes or deletes" in the reference write. They need
goals:write, funnels:write or annotations:write, and their body is the request body of their REST route.
Goal bodies
create_goal takes { name, condition, icon? } and update_goal any of these fields (at least one). name has 1 to
100 characters; icon is one character or null; condition is one of:
kind | Fields |
|---|---|
page | path (a glob: * matches one path segment, ** any run), hostname?, props? |
event | name (the custom event's name), hostname?, props? |
outbound, click, form, copy | match? (a glob), hostname? |
props is a list of up to 10 property conditions { key, op, value }, with op one of eq, neq, contains,
gt, lt. A site holds at most 100 goals. The full schema is in the API reference:
create a goal, update a goal.
{
"siteId": "SITE_ID",
"body": { "name": "Viewed pricing", "condition": { "kind": "page", "path": "/pricing" } }
}Funnel bodies
save_funnel takes { name, steps, windowSeconds? }, and analyze_funnel { steps, windowSeconds? } plus a time
window. steps holds 2 to 10 conditions of the goal form above, each with an optional label (1 to 100
characters). Without windowSeconds, a funnel counts the steps within one session; with it (1 to 7,776,000 seconds),
it follows each user across sessions and keeps the steps within that many seconds of the first. analyze_funnel only
reads: it reports the conversion of steps you have not saved. See analyze a funnel
and save a funnel.
{
"siteId": "SITE_ID",
"last": "7d",
"body": {
"steps": [
{ "kind": "page", "path": "/" },
{ "kind": "page", "path": "/pricing", "label": "Pricing" }
]
}
}{
"data": {
"unit": "sessions",
"windowSeconds": null,
"steps": [
{ "step": 1, "label": null, "condition": { "kind": "page", "path": "/" }, "count": 95, "conversion": 100, "dropoff": null, "median_seconds": null },
{ "step": 2, "label": "Pricing", "condition": { "kind": "page", "path": "/pricing" }, "count": 24, "conversion": 25.263157894736842, "dropoff": 74.73684210526316, "median_seconds": 108.122 }
]
},
"meta": { "window": { "from": "2026-10-20T20:31:17.841Z", "to": "2026-10-27T20:31:17.841Z", "tz": "UTC", "bucket": "day" } }
}Annotation bodies
create_annotation takes the annotation's own fields only: title, startsAt and optionally description,
endsAt, color and icon (see its reference). The annotation is always private to the
organization's members and shown on this site only.
{
"siteId": "SITE_ID",
"body": { "title": "Pricing page redesign", "startsAt": "2026-10-20T09:00:00Z", "color": "blue" }
}Safety
-
Writes are marked. Every tool carries MCP tool annotations, hints a client can use to ask before it acts:
Annotation Meaning here readOnlyHint: trueReads only. Every GETtool, andanalyze_funnel, which is aPOSTthat only reads.readOnlyHint: false,destructiveHint: falseAdds something and changes nothing that exists (the create and save tools). readOnlyHint: false,destructiveHint: trueChanges or deletes something that exists (the update and delete tools). openWorldHint: falseOn every tool: none reaches anything outside PilotFox. Annotations are hints for the client. What a tool may do is decided by the credential's permissions, checked by the REST route on every call.
-
create_annotationcannot publish. It has noisPublicand noscope, and refuses them (Input validation error: … Unrecognized key: "isPublic") before anything is stored. The REST route refuses a public or organization-wide annotation from any MCP call as well. So text a model read in a tool result can never reach share links, public-site visitors or the organization's other sites through MCP. -
Nothing beyond goals, funnels and annotations. No tool manages sites, members, API keys, share links, settings or data deletion.
-
Give assistants read-only credentials unless they must write, and prefer clients that ask before a write. A model can be steered by text your visitors sent; see Tool results are data, not instructions.
Tool reference
| Tool | Permission | Effect | What it does |
|---|---|---|---|
list_sites | site:read | read-only | The sites this credential can see. |
get_site | analytics:read | read-only | One site: name, domain, time zone, settings. |
overview | analytics:read | read-only | Visitors, sessions, pageviews, bounce, duration. |
timeseries | analytics:read | read-only | Metrics per time bucket. |
breakdown | analytics:read | read-only | Top values of one dimension. |
pages | analytics:read | read-only | Pages with entries, exits, bounce and time on page. |
live | analytics:read | read-only | Visitors right now, top pages, referrers, countries. |
geo_countries | analytics:read | read-only | Visitors per country. |
geo_points | analytics:read | read-only | Visitors binned on a map grid. |
events | sessions:read | read-only | The raw event list, newest first. |
event_names | analytics:read | read-only | Custom event names with counts. |
errors | analytics:read | read-only | JavaScript errors grouped by fingerprint. |
vitals | analytics:read | read-only | Core Web Vitals percentiles. |
sessions | sessions:read | read-only | Sessions, newest first. |
session | sessions:read | read-only | One session with its events. |
users | users:read | read-only | Visitors and identified users. |
user | users:read | read-only | One user's profile and traits. |
goals | goals:read | read-only | The site's goals. |
create_goal | goals:write | adds | Creates a goal: {name, condition, icon?}. |
update_goal | goals:write | changes or deletes | Updates a goal. |
delete_goal | goals:write | changes or deletes | Deletes a goal. |
funnels | funnels:read | read-only | The site's saved funnels. |
analyze_funnel | funnels:read | read-only | Conversion through steps: {steps}. |
save_funnel | funnels:write | adds | Saves a funnel: {name, steps}. |
delete_funnel | funnels:write | changes or deletes | Deletes a funnel. |
journeys | analytics:read | read-only | The most common page paths. |
retention | analytics:read | read-only | Cohort retention. |
revenue_summary | revenue:read | read-only | Revenue, payments, refunds, average order and conversion of paying visitors, in one currency. |
revenue_breakdown | revenue:read | read-only | Revenue by one dimension of the paying visit (channel, referrer_domain, utm_source, country…) or affiliate. |
segments | segments:read | read-only | Saved segments; pass one's id as segment. |
annotations | analytics:read | read-only | Chart annotations. |
create_annotation | annotations:write | adds | Creates a private annotation on this site's charts: {title, startsAt, description?, endsAt?, color?, icon?}. |
list_sites
The sites this credential can see.
- Permission:
site:read - Runs:
GET /api/v1/orgs/{orgId}/sites, withorgIdset to the credential's organization - Annotations:
readOnlyHint: true,openWorldHint: false
No arguments.
get_site
One site: name, domain, time zone, settings.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId} - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
overview
Visitors, sessions, pageviews, bounce, duration.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/overview - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
compare | "previous" | "year" | no | Also the previous period of the same length, or the same period a year earlier, under previous |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
timeseries
Metrics per time bucket.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/timeseries - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
compare | "previous" | "year" | no | Also the previous period of the same length, or the same period a year earlier, under previous |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
metrics | string | no | Comma list of metrics (default all) |
breakdown
Top values of one dimension.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/breakdown - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
limit | integer | no | Rows per page (default 50) |
offset | integer | no | Rows to skip, for the next page (default 0) |
dim | string | yes | Dimension, e.g. country, browser, referrer_domain, utm_source |
metric | string | no | Ranks the values: visitors (default), sessions, pageviews, bounce_rate or avg_session_duration |
pages
Pages with entries, exits, bounce and time on page.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/pages - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
limit | integer | no | Rows per page (default 50) |
offset | integer | no | Rows to skip, for the next page (default 0) |
view | string | no | all, entry or exit |
group | string | no | page or section |
search | string | no | Case-insensitive match on the pathname or the title |
live
Visitors right now, top pages, referrers, countries.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/live - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
geo_countries
Visitors per country.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/geo/countries - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
geo_points
Visitors binned on a map grid.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/geo/points - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
bin | string | yes | h3 (H3 cells, sized by res) or grid (a latitude/longitude grid, sized by precision) |
res | integer | no | With bin h3: the H3 resolution, 2 to 7 (default 4) |
precision | integer | no | With bin grid: decimals of the grid's degrees, 0 to 2 (default 1) |
events
The raw event list, newest first.
- Permission:
sessions:read - Runs:
GET /api/v1/sites/{siteId}/events - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
cursor | string | no | meta.nextCursor of the previous page |
limit | integer | no | Rows per page (default 50) |
since | string | no | Instead of a window, the realtime tail: events since this ISO instant (≤ 24 h) |
event_names
Custom event names with counts.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/events/names - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
limit | integer | no | At most this many names (default 50) |
errors
JavaScript errors grouped by fingerprint.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/errors - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
limit | integer | no | Rows per page (default 50) |
offset | integer | no | Rows to skip, for the next page (default 0) |
vitals
Core Web Vitals percentiles.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/vitals - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
sessions
Sessions, newest first.
- Permission:
sessions:read - Runs:
GET /api/v1/sites/{siteId}/sessions - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
cursor | string | no | meta.nextCursor of the previous page |
limit | integer | no | Rows per page (default 50) |
sort | string | no | Largest or latest first by: started (default), ended, duration, pageviews or events |
view | string | no | all (default), identified, with_replay, converted, bounced or with_errors |
search | string | no | Substring of the user id, entry or exit page or referrer domain; the user's name or email with users:read |
session
One session with its events.
- Permission:
sessions:read - Runs:
GET /api/v1/sites/{siteId}/sessions/{sessionId} - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
sessionId | string | yes | A session's session_id (sessions) |
users
Visitors and identified users.
- Permission:
users:read - Runs:
GET /api/v1/sites/{siteId}/users - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
cursor | string | no | meta.nextCursor of the previous page |
limit | integer | no | Rows per page (default 50) |
sort | string | no | Largest or latest first by: last_seen (default), first_seen, sessions, pageviews or events |
search | string | no | Case-insensitive substring of the user id, or of the user's name or email trait |
user
One user's profile and traits.
- Permission:
users:read - Runs:
GET /api/v1/sites/{siteId}/users/{userId} - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
userId | string | yes | A user's user_id (users) |
goals
The site's goals.
- Permission:
goals:read - Runs:
GET /api/v1/sites/{siteId}/goals - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
create_goal
Creates a goal: {name, condition, icon?}.
- Permission:
goals:write - Runs:
POST /api/v1/sites/{siteId}/goals - Annotations:
readOnlyHint: false,destructiveHint: false,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
body | object | yes | The JSON request body of the REST route |
update_goal
Updates a goal.
- Permission:
goals:write - Runs:
PATCH /api/v1/sites/{siteId}/goals/{id} - Annotations:
readOnlyHint: false,destructiveHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
id | string | yes | The definition's id |
body | object | yes | The JSON request body of the REST route |
delete_goal
Deletes a goal.
- Permission:
goals:write - Runs:
DELETE /api/v1/sites/{siteId}/goals/{id} - Annotations:
readOnlyHint: false,destructiveHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
id | string | yes | The definition's id |
funnels
The site's saved funnels.
- Permission:
funnels:read - Runs:
GET /api/v1/sites/{siteId}/funnels - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
analyze_funnel
Conversion through steps: {steps}.
- Permission:
funnels:read - Runs:
POST /api/v1/sites/{siteId}/funnels/analyze - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
body | object | yes | The JSON request body of the REST route |
save_funnel
Saves a funnel: {name, steps}.
- Permission:
funnels:write - Runs:
POST /api/v1/sites/{siteId}/funnels - Annotations:
readOnlyHint: false,destructiveHint: false,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
body | object | yes | The JSON request body of the REST route |
delete_funnel
Deletes a funnel.
- Permission:
funnels:write - Runs:
DELETE /api/v1/sites/{siteId}/funnels/{id} - Annotations:
readOnlyHint: false,destructiveHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
id | string | yes | The definition's id |
journeys
The most common page paths.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/journeys - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
steps | integer | no | Pages per path, 2 to 10 (default 3) |
start_page | string | no | A path glob such as /blog/**: each path starts at the session's first page matching it |
end_page | string | no | A path glob: each path ends at its first page matching it, within steps; sessions without one drop out |
group | string | no | page (default) or section (/a/b/c counts as /a/**) |
retention
Cohort retention.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/retention - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
unit | string | no | day, week (default) or month |
periods | integer | no | Offset columns (0, 1, 2, … periods later), at most 92 days, 53 weeks or 24 months; default one per cohort |
revenue_summary
Revenue, payments, refunds, average order and conversion of paying visitors, in one currency.
- Permission:
revenue:read - Runs:
GET /api/v1/sites/{siteId}/revenue/summary - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
compare | "previous" | "year" | no | Also the previous period of the same length, or the same period a year earlier, under previous |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
currency | string | no | ISO 4217 code; every amount is in its minor unit; default the site's |
revenue_breakdown
Revenue by one dimension of the paying visit (channel, referrer_domain, utm_source, country…) or affiliate.
- Permission:
revenue:read - Runs:
GET /api/v1/sites/{siteId}/revenue/breakdown - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
filters | { dim: string; op: string; values: (string | number | boolean)[] }[] | no | Filters, ANDed, e.g. [{dim: 'country', op: 'eq', values: ['US']}] |
segment | string | no | A saved segment's id (segments): its filters apply |
limit | integer | no | Rows per page (default 50) |
offset | integer | no | Rows to skip, for the next page (default 0) |
currency | string | no | ISO 4217 code; every amount is in its minor unit; default the site's |
dim | string | yes | A breakdown dimension (as breakdown), or affiliate |
segments
Saved segments; pass one's id as segment.
- Permission:
segments:read - Runs:
GET /api/v1/sites/{siteId}/segments - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
annotations
Chart annotations.
- Permission:
analytics:read - Runs:
GET /api/v1/sites/{siteId}/annotations - Annotations:
readOnlyHint: true,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
start | string | no | Window start: a date (YYYY-MM-DD, in tz) or an ISO instant; with end. Every call needs exactly one window: start+end, last or all |
end | string | no | Window end: a date (inclusive) or an ISO instant (exclusive); with start |
last | string | no | Instead of start/end: the last 30m, 12h, 7d, … (at most 400d) |
tz | string | no | IANA time zone; default the site's |
all | boolean | no | true: all time instead of a window |
bucket | "minute" | "5m" | "15m" | "hour" | "day" | "week" | "month" | "year" | no | Time bucket of the answer's series or trends; default from the window's length |
create_annotation
Creates a private annotation on this site's charts: {title, startsAt, description?, endsAt?, color?, icon?}.
- Permission:
annotations:write - Runs:
POST /api/v1/sites/{siteId}/annotations - Annotations:
readOnlyHint: false,destructiveHint: false,openWorldHint: false
| Argument | Type | Required | Description |
|---|---|---|---|
siteId | string | yes | The site's public id (list_sites) |
body | object | yes | The annotation: private to the organization's members, on this site only. No other fields. |
body.title | string | yes | Up to 120 characters |
body.description | string | null | no | Up to 2000 characters |
body.startsAt | string | yes | An ISO instant with offset, e.g. 2026-10-01T09:00:00Z |
body.endsAt | string | null | no | For a range: an ISO instant after startsAt |
body.color | "blue" | "salmon" | "green" | "amber" | "red" | "violet" | "gray" | null | no | |
body.icon | string | null | no | One character, e.g. an emoji |
Connect with OAuth
Sign in with PilotFox from an MCP client that supports MCP authorization: discovery, client ids, organization choice, consent, scopes, token checks and revocation.
Revenue overview
Connect a payment provider with a read-only key, see which payments count, and read revenue and subscription metrics.