Skip to content

API keys and personal access tokens

You will learn stream ingest keys versus personal access tokens for REST and MCP.

Cordo uses two different secret types. Mixing them up is the most common auth mistake: ingest keys cannot query, and personal tokens cannot ship production logs.

Quick comparison

Stream API key Personal access token
Format lax_<prefix>_<secret> lxp_<prefix>_<secret>
Purpose Ingest only REST API + MCP (manage, query, views, agent tools)
Where in the app Streams → API keys (selected stream) Settings → API tokens
Create control New API key New token
Shown once Yes Yes
Lifetime Required; standard 90-day default, with a deployment-configured maximum capped at one year Optional expiry
Least privilege One stream; optional native / OTLP / OpenSearch scopes and event ceiling REST/MCP scopes plus optional stream allowlist
Rotation Successor with 0–3600 second overlap Successor with 0–3600 second overlap

Stream API keys

Use these to send events into one stream.

  • Native HTTP JSON: header X-API-Key: lax_…
  • OpenSearch-compatible _bulk: HTTP Basic — username ignored, password is the API key
  • OTLP Logs: X-API-Key or Authorization: Bearer lax_…

Every new key expires. Keys can be restricted to only the ingest surfaces a collector uses and can carry a lower events-per-minute ceiling. The key table shows last use, expiry, rotation state, and stale/expiring/recent rate-limit warnings. Rotate before expiry with a safe overlap so collectors can switch without dropping logs.

Existing keys were given one bounded 90-day migration window when expiration shipped; rotate them to establish fresh lineage and settings.

Session JWTs from the web app cannot ingest. Expiry, overlap completion, or manual revocation stops authentication with that secret. These checks use the server database clock, not the collector clock.

Hands-on: Manage stream API keys.

Personal access tokens

Use these for scripts, Cursor, and other MCP clients.

  • Header: Authorization: Bearer lxp_…
  • Permissions: least-privilege scopes (or full access), optionally limited to selected streams you can already access
  • Stream and saved-view inventories apply that limit before pagination, sidebar ordering, and name resolution, so hidden rows cannot change counts, cursors, or ambiguity candidates
  • Stream limits follow indirect references too: a saved view can be created, opened, previewed, or exported only when its home stream and every persisted stream_id are in the token allowlist; Cordo never returns a partial result
  • Reading saved-view definitions uses events:read; previewing or mutating a view, reading/changing its table layout, or changing sidebar order uses views:write
  • Cannot call HTTP ingest endpoints (/ingest, OpenSearch bulk, OTLP) — use a stream API key for production shippers; MCP ingest_events needs the events:ingest scope
  • Cannot create, rotate, or broaden another PAT (interactive login required)
  • Platform operators using PATs need ops:read (or full access *) for /v1/ops/*; the scope is platform-wide but never grants operator status by itself

Existing tokens created before scopes shipped keep full access (*) so agents keep working until you rotate them to narrower scopes.

Hands-on: Manage personal access tokens and Connect MCP.

Next