Documentation
Set up Privatrak, name what matters to you and read every report.
MCP reference
Everything developers need about Privatrak’s MCP server: the address, sign-in and API keys, every tool and prompt, and how changes are grouped so they undo in one step. To connect an assistant, start with Connect an AI assistant.
Endpoint and transport
- URL:
https://api.privatrak.com/mcp. One address serves all your projects. A connection made by signing in can cover several projects, an API key exactly one. - Transport: Streamable HTTP. The server accepts every protocol version from
2024-11-05to2026-07-28. Send every message as aPOSTwithContent-Type: application/jsonandAccept: application/json, text/event-stream. Without both types inAcceptthe server answers400. Answers come back as plain JSON. - Stateless: the server keeps nothing between requests and issues no
Mcp-Session-Id, so every request stands on its own.GETandDELETEon/mcpanswer405, because there is no session stream to open or close. - CORS:
/mcpallows any origin, without credentials, and request headers includingAuthorization,X-API-Key,Mcp-Protocol-VersionandMcp-Session-Id. It exposesWWW-Authenticate, so browser-based clients such as MCP Inspector can start the OAuth flow.
Authentication
/mcp accepts two kinds of credential: an OAuth access token, issued when a user signs in, or one of a project’s API keys. Send either as Authorization: Bearer TOKEN.
OAuth
- Flow: OAuth 2.1 authorization code with PKCE,
S256only. Clients are public (token_endpoint_auth_method: none). There is no client secret and no implicit, password or client credentials grant. - Discovery: every
401from/mcpnames the protected resource metadata (RFC 9728) inWWW-Authenticate, athttps://api.privatrak.com/.well-known/oauth-protected-resource/mcp. It points to the authorization server, whose metadata (RFC 8414) is at/.well-known/oauth-authorization-server. The endpoints are/oauth/authorize,/oauth/token,/oauth/registerand/oauth/revoke. - Client registration: a Client ID Metadata Document, meaning an
httpsclient_idwhose URL serves the client’s metadata, or Dynamic Client Registration (RFC 7591) at/oauth/register. Claude uses the first, Cursor and Codex the second. - Scopes:
readandwrite, wherewriteincludesread. A request without a scope getsread.offline_accessis accepted and changes nothing, because every connection gets a refresh token. - Consent: the user chooses the projects, either All my projects (including ones created or joined later) or Only these, and the access, Read or Read and write. Read and write is offered when the client asked for
write. What the user sees is described in Connect an AI assistant. - Tokens: access tokens last 1 hour and are bound to the resource
https://api.privatrak.com/mcp(RFC 8707), so the REST API does not accept them. Refresh tokens rotate on every use and expire after 30 days unused. - Access:
readgets the same tools as an AI assistant key.writeadds the write tools, the setup run tools and thesetup_projectprompt, the same write access as a full access key. - Revocation: the user disconnects a client under Settings › Account › Connected AI apps, which ends its tokens at once. A client can revoke its own tokens at
/oauth/revoke(RFC 7009).
API keys
Send one of a project’s API keys as Authorization: Bearer YOUR_KEY or X-API-Key: YOUR_KEY. Both headers with different keys are a 401. A key belongs to one project, and what it may do depends on its kind:
- Your AI assistant (type
read): every read tool, theproduct_reviewprompt and the guide. It can change nothing, and it cannot see members, billing or other keys. - Full access (type
secret): also every write tool, the setup run tools and thesetup_projectprompt. - Your website (type
public): refused, because it sits in your page source where anyone can read it.
These requests get 401: no credential, an unknown, expired or revoked one, or a website key. The answer carries WWW-Authenticate: Bearer realm="privatrak" and the resource_metadata URL, plus error="invalid_token" when a credential was sent. The JSON body’s error says what was wrong and its error_code is auth_invalid_token. If the credential cannot be checked because the service is briefly unavailable, the answer is 503, and retrying is safe.
Projects
Every tool and prompt that answers about one project takes an optional project argument, a project ID from list_projects. The server keeps no current project between calls.
- A connection that covers one project may leave it out, and so may every API key. An API key refuses any project but its own.
- When a connection covers more than one project, a call without
projectis a tool error that lists them, so the assistant asks the user instead of guessing. Project membership is checked on every call. list_projectsreturns each project’s ID, name, timezone and access. For an API key it lists the key’s one project.
Rate limit
Each API key and each connection may send a burst of 60 requests, refilled at one request every two seconds. Beyond that the server answers 429 with Retry-After: 2 and error_code mcp_rate_limited. Every request counts, tools/list included.
Data conventions
- Dates are
YYYY-MM-DDcalendar days in the project’s timezone, whichget_projectreturns. Timestamps in answers are UTC. - Default range: the last 28 complete days, ending yesterday. Every answer says which period it used in
period. Atoin the future is cut at today and markedclamped_to_today. Afromafter today is an error. - Coverage: a period that starts before the project has complete data is marked
starts_before_datawithdata_from, and no percentage change is computed against it. Week and month buckets that cover only part of their span are markedpartial. - Counts:
unique_sessionsare visitors counted once per day, so over a longer range one person can be counted on several days. Counts fromlist_elements,preview_feature_ruleandpreview_funnelare events, not visitors. Counts fromlist_click_problemsare page views: each problem counts once per element per page view. - Range cap: those three tools read raw events and accept at most 92 days per call, and so does
list_click_problems. - Hours:
run_insights_querywithgroup_byhourandget_feature_timeserieswithgranularityhourreturn the hours of the project’s timezone. A day the clocks change has 23 or 25 hours. Both accept at most 31 days,fromandtoincluded.
Results and errors
- A tool result carries its data as
structuredContentand the same JSON as text, for clients that only read text. No output schemas are published. - When the API refuses a call, for example an unknown funnel or an invalid filter, the result has
isError: trueand the API’s own message as text, so the assistant can correct itself. - Arguments are checked against the tool’s input schema before anything runs. Unknown arguments are rejected, not ignored, and an unknown tool is a JSON-RPC error.
Tools
Your client gets every tool with its full input schema from tools/list. The tools carry MCP’s hints: reading tools are marked readOnlyHint, and tools that change or delete something are marked destructiveHint, which clients use to ask you before running them. openWorldHint is false throughout, because the tools only ever touch the one project.
Reading, with every connection and key
| Tool | What it does |
|---|---|
list_projects | The projects this connection can use, with each one’s ID, name, timezone and access. |
get_project | One project: name, timezone, plan, retention, today’s date, limits and slots left. |
get_overview | Headline numbers against the previous period, a daily series, top pages and features. |
list_features | Features ranked by use, optionally compared with the previous period, including features that stopped. |
get_feature_timeseries | Use of the top features per day or per hour. |
list_funnels | Saved funnels with a rough conversion over the last 31 days. |
get_funnel | One funnel’s steps for a period, with drop-off and the worst step. |
get_funnel_trend | A funnel’s conversion per day, week, month or year. |
preview_funnel | How many events each draft step matches, before a funnel is created. |
list_sources | Where visitors came from, most first: search engines, social media, AI assistants, other websites and campaign links. Visitors who came directly or from an unknown place share one row. It also lists the pages they landed on. Each visitor counts once a day, for their first page that day. |
list_campaigns | Visitors who came through your campaign links, most first. One row per campaign name, source and medium, with its top link variants, search terms and the pages they landed on. |
list_pages | Pages ranked by views, with entries: how many visitors started their day on each page. Views count page views, not visitors. A page is its site address plus its path, so addresses that differ only after a question mark are one page. |
get_flow | What visitors did next, as a tree of up to four steps. |
find_flow_nodes | Search the pages and features that appear in user paths. |
run_insights_query | Count events per hour, day, week or month, with filters and one breakdown. |
list_dashboards | Saved dashboards. |
get_dashboard | One dashboard and its widgets’ queries. |
list_feature_rules | The rules that name events as features. |
preview_feature_rule | What a draft rule would count, and which other features already count some of the same events. |
list_elements | What people clicked and submitted, grouped, with the features each counts towards. |
list_click_problems | Elements where clicks went wrong (no effect, repeated clicks, errors, slow responses), worst first, with the status of each. Each problem counts once per page view. |
list_property_keys | Properties available for filters and breakdowns. |
list_property_values | The values a property has taken. |
search_docs | Search Privatrak’s documentation and get the matching sections with links. |
read_docs_page | Read one documentation page in full. |
Changing, with write access
Write access has two sources: an OAuth connection where the user chose Read and write, or a full access key. Both allow the same tools.
| Tool | What it does | Marked destructive |
|---|---|---|
create_feature_rule | Save a feature rule. | no |
update_feature_rule | Replace a rule’s name and conditions. | yes |
delete_feature_rule | Delete a rule. | yes |
create_funnel | Save a funnel of 2 to 10 steps. | no |
rename_funnel | Rename a funnel. Its steps cannot change. | yes |
delete_funnel | Delete a funnel and the days it has counted. | yes |
create_dashboard | Create an empty dashboard. | no |
rename_dashboard | Rename a dashboard. | yes |
delete_dashboard | Delete a dashboard and its widgets. | yes |
add_widget | Add a chart to a dashboard. | no |
update_widget | Change a widget’s title, query or position. | yes |
remove_widget | Remove a widget. | yes |
resolve_click_problem | Mark a click problem as fixed. If a click on it does nothing again or causes an error again, it comes back as Happened again. | yes |
ignore_click_problem | Mark a click problem as meant to be like that. It stays hidden for good. | yes |
reopen_click_problem | Take back resolving or ignoring, so the element is listed as open again. | yes |
Setup runs, with write access
| Tool | What it does | Marked destructive |
|---|---|---|
start_setup_run | Start a group of changes that undoes in one step. | no |
list_setup_runs | Earlier setup runs and what each changed. | no |
get_setup_run | One run’s changes, before and after. | no |
undo_setup_run | Undo a run, as a dry run by default. | yes |
The three click problem tools take a row’s identity from list_click_problems: all five fields exactly as returned, null where it is null. An identity with a field left out is refused, because it would name no real element. Rows listed with group_by: selector carry no identity. Resolving and ignoring only hide a row. No count changes, and no event is deleted.
Prompts and resources
product_review, with the optional argumentsfrom,toandproject, in that order: a fixed review of the overall change, the features that moved most, funnel drop-offs, the most-used elements no feature counts yet, and what to look at next. The review is described in Ask about your data. Every connection and key.setup_project, with the optional argumentsinstrument,focusandproject, in that order.instrumentiscode(data-trackin the code, as a change to review),rules(feature rules only: the code is never edited, and no change to it is proposed) or empty to let the assistant choose.focusnames what matters most. The method is described in Let it set up your project. Write access only.privatrak://guide/instrumentation(Markdown): how the tracker names things, how feature rules differ fromdata-track, and the limits on funnels and dashboards. Every key.
When a client connects, the server also sends instructions that tell the assistant to call get_project first and how to read the numbers honestly. For an OAuth connection they also name the projects it covers.
Setup runs
A setup run groups the changes an assistant makes, so they undo in one step. Every change made through a write tool belongs to one.
- Start one with
start_setup_runand pass itsrun_idto every write. A write withoutrun_idstarts a run of kindchatand returns its id withrun_started: true. A write that fails removes the run it started. - Over the REST API the same thing is the header
X-Setup-Run-IDon any write to feature rules, funnels, dashboards, widgets or click problems. Writes without it are not recorded and cannot be undone this way. undo_setup_rundefaults todry_run: true, which reports what would happen and changes nothing. The real undo works newest first in one transaction: it removes what the run created, puts back what it changed, and recreates what it deleted with its original id. Anything someone else changed since is skipped and reported. A run can be undone once, and after that it takes no more changes. In the dashboard, the same undo is on the AI assistant changes page.- A funnel’s counted days cannot be restored. The undo reports them as
funnel_historyandfunnel_days_lostbefore and after. - A click problem the run resolved or ignored is open again after the undo, and one the run reopened, or switched between resolved and ignored, is put back as it was. If someone resolved, ignored or reopened it since, the undo leaves it alone and reports it.
- Plan limits apply to writes and to undo alike.
get_projectreturnsslots_leftfor feature rules, funnels, dashboards and widgets.
Keys in your assistant’s settings
These settings apply to assistants connected with an API key. A connection made by signing in covers every project the user allowed and needs none of this. Each key belongs to one project, so an assistant should use the key of the project whose code it works on.
- Claude Code saves the connection for the folder you run
claude mcp addin. Run it in each project’s folder with that project’s key. - Cursor reads
.cursor/mcp.jsonfrom each project’s folder, so each project has its own file and key. - Codex uses one connection in every folder unless a project names its own. Pick a variable name for each project, such as
PRIVATRAK_API_KEY_SHOP, and save that project’s key under it as forPRIVATRAK_API_KEY. Then create.codex/config.tomlin the project’s folder:
[mcp_servers.privatrak]
url = "https://api.privatrak.com/mcp"
bearer_token_env_var = "PRIVATRAK_API_KEY_SHOP"The file names the variable, not the key, so it can go into your code repository. Codex reads it only in a folder you trust, and asks you about that the first time you start it there.
Switching keys
An assistant registers Privatrak under the name privatrak, so it holds one key at a time. To switch between an AI assistant key and a full access key, replace the old key rather than adding a second one. In Claude Code, run claude mcp remove privatrak in the project’s folder and then the new command. In Codex, change the key in the line that saves it. In Cursor, replace the key in mcp.json.
To sign in instead of using a key, first remove the connection that uses the key: claude mcp remove privatrak in Claude Code, codex mcp remove privatrak in Codex. In Cursor, replace the privatrak entry in mcp.json with the one from Connect an AI assistant. Then sign in as described there, and revoke the old key on the API Keys page.