API Reference

Authentication

How to authenticate with the Trenchcoat API using API keys and understand rate limits.

Getting an API Key

API keys are managed from your Trenchcoat dashboard. To create one:

  1. Sign in at app.trenchcoat.io
  2. Navigate to Settings → API Keys
  3. Click Create API Key, give it a name, and select the scopes it needs
  4. Copy the key immediately — it is shown only once

Keys follow the format ct_live_ followed by a random string, for example:

ct_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

Store keys in environment variables, never in source code or version control.


Authenticating Requests

Include your API key in the X-API-Key header on every request:

curl https://app.trenchcoat.io/api/v1/sessions \
  -H "X-API-Key: ct_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

There is no other authentication mechanism. The header is required on all endpoints.


Scopes

Scopes describe what Trenchcoat records, from the account owner's point of view. They are not HTTP-verb permissions — there is no separate "write" scope, and every capture level can send data.

ScopeRecords
read:sessionsConversations: timing, model, tokens, cost. No content.
read:eventsThe above, plus which skills, subagents, MCPs, tools, and commands ran — by name — plus error diagnostics
read:promptsThe above, plus prompt and response text, verbatim
read:fullThe above, plus tool arguments, shell commands, file paths, and raw API bodies
adminUnrestricted; bypasses every check

Each level implies the ones below it. read:full implies read:prompts, read:events, and read:sessions; read:prompts and read:events each imply read:sessions.

That implication is easy to get wrong when reading a key's scopes programmatically. GET /api/v1/keys/self returns the scopes as stored, so a full-capture key reports exactly ["read:full"] — not the four levels it effectively grants. Expand before comparing.

What each endpoint requires

EndpointRequired scope
POST /api/v1/eventsread:sessions
POST /api/v1/otel/v1/logsread:sessions
GET /api/v1/sessionsread:sessions
GET /api/v1/analytics/overviewread:sessions
POST /api/v1/evals/scoresread:sessions
GET /api/v1/analytics/toolsread:events
GET /api/v1/keys/selfnone — any valid key

Ingestion requires only read:sessions, so a key at any capture level can send events. The level decides what survives ingestion, not whether it is accepted.

Content is stripped server-side

Content above a key's level is removed before the row is written. A client that sends prompt text with a read:sessions key is not rejected — the text is simply dropped.

This is why the plugin's local OTEL_LOG_* variables cannot widen what is captured: they control what Claude Code sends, while the key's scopes control what is stored. To capture more, change the key's scopes.

Two things that are easy to get wrong

Dashboard visibility is not scope-governed. Dashboard pages use your logged-in session and row-level security, never an API key. A scope determines whether data was recorded, not whether a page can display it.

Component names and tool arguments split across two levels. Which skill or tool ran resolves at read:events; the arguments it was called with require read:full. Claude Code's own OTEL_LOG_TOOL_DETAILS flag covers both together, so that one flag is justified by either level.


Rate Limits

Limits are applied per API key over a 60-second window. Exceeding one returns 429.

TierRequests / minute
Standard60
Premium200
Ingestion200

Keys used for event ingestion are placed on the Ingestion tier. A single request may carry up to 1000 events, so the effective event throughput is far higher than the request limit suggests.


Error Responses

401 — Invalid or Missing Key

Returned when the X-API-Key header is absent or the key does not exist.

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or invalid API key."
  }
}

403 — Insufficient Scope

Returned when the key exists but does not have the scope required by the endpoint.

{
  "error": {
    "code": "FORBIDDEN",
    "message": "This API key does not have the required scope: read:events"
  }
}

429 — Rate Limit Exceeded

Returned when the key has sent too many requests within the current window.

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 60 seconds."
  }
}

The response includes a Retry-After header indicating how many seconds to wait before retrying.

On this page