vTilt
By roleFoundersKnow what to fix firstMarketersSee what happens after the clickCMOsSee what your budget really buys
By businessSaaSTurn signups into active usersEcommerceSee why shoppers abandon cartsAgenciesProve your work to clientsEnterpriseOne customer record for every team
MeasureWeb AnalyticsFind where buyers leaveSession ReplaySee why they left, not just that they didAdsKnow what paid visitors do nextIntegrationsOne snippet feeds your stack
ThinkPeopleOne profile per customerCustomer MemoryvTilt remembers every visitorSite KnowledgeAnswers and audits from your own pages
ActAsk AIAnswers from real behaviorAI ChatChat that knows the visitorEmail CampaignsEmail from real visits
PricingWhy vTiltDocs
Docs / Authentication
Getting Started
OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes
Guides
Event forwardingReverse proxyRealtime dashboardSite knowledge
Frontend frameworks
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus
Backend frameworks
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails
Backend languages
PythonPHPRubyElixirGoJava.NET / C#Rust
Stack guides
Vue + PHP
API Reference
Browser SDK
Script bundlesAutocaptureWeb VitalsSession recordingChat widgetFeature readinessRemote configurationDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdownGlobal properties & opt-outError tracking
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceSite knowledgeGoogle AdsMeta Ads
Client setup
CursorClaude DesktopVS CodeCodex

Getting Started

OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes

Guides

Event forwardingReverse proxyRealtime dashboardSite knowledge

API Reference

MCP server

OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceSite knowledgeGoogle AdsMeta Ads
On this page

On this page

  • 1. Create a key
  • 2. What a key can do
  • 3. Use a key
  • 4. Pin a project
  • 5. Rotate and revoke
  • 6. Common errors
  • Next steps
DocsMCP serverGuidesAuthentication

Authentication

Last updated July 22, 2026

Authenticate MCP clients with vTilt using OAuth 2.1 + PKCE (recommended) or a long-lived vtu_ personal API key.

Note

This page is part of the MCP server documentation. Use the documentation map to jump to OAuth, AI intelligence, prompts, or your editor's setup guide.

The vTilt MCP server accepts two equally valid authentication paths. Pick whichever fits your client:

PathBest forHeader sent on every request
OAuth 2.1 + PKCEClaude Desktop, ChatGPT Connector, Cursor, custom agents with a browserAuthorization: Bearer <JWT> (auto-refreshed)
Personal API keyShell scripts, CI jobs, curl, legacy stdio bridgesAuthorization: Bearer vtu_… (long-lived)

Both paths grant the same access to the same projects you see in the dashboard. The rest of this page covers personal API keys — for OAuth, see the dedicated OAuth guide.

Tip

Modern MCP clients (Claude Desktop, ChatGPT Connector, recent Cursor releases) auto-discover OAuth and walk you through a one-time browser sign-in. You only need a vtu_ key if your client can't run a browser flow.

Important

Personal API keys (vtu_…) are completely separate from your project token (vt_…) used by the browser SDK. The browser SDK token is public by design (it lives in your customers' page source); MCP keys grant access to your dashboard data and must never be embedded in a frontend or shared publicly.

#1. Create a key

  1. Sign in to your vTilt dashboard.
  2. Open the user menu (top-right) and choose Personal API keys, or visit Account → Personal API keys directly.
  3. Click Create key.
  4. Give it a name that identifies the client and machine — e.g. Cursor on laptop, Claude Desktop on home Mac, Codex CLI on prod-runner.
  5. Pick an access level:
    • Read-only (recommended) — the agent can list and inspect data through every read tool: get_kpis, list_persons, list_recent_events, list_recordings, the VQL query layer, and search_docs. This is the right choice for almost every workflow.
    • Read + write — adds the ability to mutate project state through write tools (update_project plus the Google Ads write surface). Every write call is recorded in the MCP audit log. Pick this only if you actively need the agent to change state on your behalf.
  6. Click Create. The full secret (vtu_…) is shown once.
  7. Copy it now and paste it into your client. After you close the modal the secret cannot be recovered.

If you lose the secret, revoke the key (it stays in the list with a revokedAt timestamp for audit purposes) and create a new one. Old keys keep working until they're revoked.

Warning

A Read + write key can mutate project state. Treat it like a database password — don't paste it into shared chats, don't commit it to git, and don't leave it in dotfiles backed up to a public service. If you only need read access, mint a read-only key — they cannot see write tools at all.

#2. What a key can do

A vtu_ key inherits all the projects you can see in the dashboard. Keys are scoped:

FieldDefault
scope.modemcp (the only value)
scope.accessread (default) or write — picked by the Read-only / Read + write radio in the create modal
scopedProjectIdsnull — every project you can access
expiresAtnull — no expiry by default

When the public REST API ships, you'll be able to mint keys with scope.mode = 'rest'; that selector isn't surfaced yet because there's no public REST surface to call.

#3. Use a key

Pass the secret as a Bearer token. Every MCP client supports headers:

# Health check — verify the key works before configuring a client.
# `tools/list` is an MCP method that returns the tool registry.
curl -X POST 'https://www.vtilt.com/api/mcp' \
  -H 'Authorization: Bearer vtu_YOUR_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'
bash

A successful response lists all tools the key can see (filtered by your access scope). If the response contains "error":{"code":-32001 or HTTP 401, the bearer header is missing, malformed, or revoked.

#4. Pin a project

If you have access to multiple projects, the server cannot guess which one you mean. Three ways to pin:

MethodWhere you set itLives until
HeaderThe MCP client's connection settingsThe connection is reset
Query parameterThe MCP server URL itselfThe connection is reset
switch_projectThe agent calls it during a conversation30 days (refreshed on use)

Optional fourth header — x-vtilt-mcp-session-id: <uuid> — partitions Redis session pins when several MCP connections share the same user credential. See MCP server overview and the Cursor guide's subsection on parallel chats.

Examples:

# Header (Cursor, Claude Desktop, VS Code all expose a "headers" field)
x-vtilt-project-id: 7c5c1a3a-9d62-4e09-8a4f-9a3ce1b1f4a8

# Optional — isolate switch_project pins per MCP connection (UUID)
x-vtilt-mcp-session-id: f47ac10b-58cc-4372-a567-0e02b2c3d479

# Query parameter
https://www.vtilt.com/api/mcp?project_id=7c5c1a3a-9d62-4e09-8a4f-9a3ce1b1f4a8

# Session pin via tool call (the agent does this for you)
> "Switch me to the Marketing site project."
text

You can find a project's id at Project settings → General in the dashboard. Single-project users don't need to pin — the server picks your only project automatically.

#5. Rotate and revoke

  • Open Account → Personal API keys.
  • Click Revoke next to the key. The row stays in the list (greyed out, with a revokedAt timestamp) so you can audit which keys existed when. Revoked keys reject every subsequent request with unauthorized.
  • To rotate, create a new key first, swap it into your client, then revoke the old one.

Tip

Treat each device as its own key. If your laptop is lost, revoking a single key shuts down only that machine; your other clients keep working.

#6. Common errors

Error responseMeaningFix
{"code":-32001,"message":"Unauthorized","data":{"reason":"missing_bearer"}}No Authorization headerConfigure your client's Authorization header
{"code":-32001,"message":"Unauthorized","data":{"reason":"malformed_bearer"}}Header present but not Bearer …Prefix with Bearer (note the space) and try again
{"code":-32001,"message":"Unauthorized","data":{"reason":"unknown_token"}}Key not in the databaseRe-copy from the dashboard, or create a new key
{"code":-32001,"message":"Unauthorized","data":{"reason":"revoked"}}Key was revokedCreate a new key
{"code":-32001,"message":"Unauthorized","data":{"reason":"expired"}}Key expiry passedCreate a new key
{"code":-32001,"message":"Unauthorized","data":{"reason":"oauth_signature_invalid"}}OAuth access token signature invalidReconnect your OAuth client
{"code":-32001,"message":"Unauthorized","data":{"reason":"oauth_jwks_not_found"}}JWKS endpoint returned 404 (wrong URL or routing)Ensure GET https://www.vtilt.com/api/auth/jwks returns 200 on this host
{"code":-32001,"message":"Unauthorized","data":{"reason":"oauth_audience_mismatch"}}OAuth token's aud ≠ this MCP endpointReconnect your OAuth client against this host
{"code":-32001,"message":"Unauthorized","data":{"reason":"oauth_expired"}}OAuth access token expiredClient should refresh automatically — reconnect if it doesn't
{"code":-32001,"message":"Unauthorized","data":{"reason":"oauth_unknown_user"}}OAuth token's sub doesn't map to a vTilt userReconnect and sign in again
{"code":-32003,"message":"Forbidden","data":{"reason":"project_not_accessible"}}Header/query pinned to a project you can't accessRemove the pin or pick a project from your dashboard
{"code":-32099,"message":"Rate limit exceeded","data":{"retryAfter":42}}60-req/min sliding window exceededBack off and try again after retryAfter seconds

#Next steps

  • OAuth — the browser sign-in flow for modern MCP clients (recommended).
  • Cursor — install in 30 seconds via the Cursor MCP catalogue or ~/.cursor/mcp.json.
  • Claude Desktop — connect via OAuth or the mcp-remote bridge.
  • VS Code — drop into .vscode/mcp.json.
  • Codex — codex mcp add one-liner.
PreviousOverviewMCP serverNextOAuthMCP server