vTilt
Why vTiltHow It WorksFeaturesFAQDocs
Docs / Authentication
Quick startEvent forwarding
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceGoogle Ads
Client setup
CursorClaude DesktopVS CodeCodex
Realtime
Debug ViewRealtime Dashboard
Integration guides
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
SDK
Browser SDK
InstallScript bundlesEvent trackingAutocaptureIdentify & aliasWeb VitalsSession recordingChat widgetFeature readinessRemote configurationReverse proxyDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdown

Documentation

vTilt
Quick startEvent forwarding

MCP server

OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceGoogle Ads

Realtime

Debug ViewRealtime Dashboard

Integration guides

SDK

Authentication

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

Note

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

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

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: kpis-get, persons-list, events-recent, recordings-list, the VQL query layer, and docs-search. This is the right choice for almost every workflow.
    • Read + write — adds the ability to mutate project state through write tools (project-update today; more on the way). 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

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

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.
PreviousOverviewNextOAuth

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