Skip to Content
Platform
SDK & CLIAuthentication & API Keys

Authentication & API keys

How each client proves who it is: runtime API keys for server-side code, member login for the CLI, and a token broker for browsers.

Server-side SDK code authenticates with an API key. The CLI uses a browser-approved member session. Browser code never holds either credential; it uses a short-lived session or token minted by your backend. Every credential resolves to a project and an environment server-side.

API keys

An API key is a long-lived credential that server-side SDK code and automation use to run agents. Each key:

  • is scoped to one environment (development, staging, or production) so test and live traffic stay cleanly separated;
  • carries the platform’s runtimes capability;
  • is shown once at creation and stored hashed, so copy it immediately, because it can’t be retrieved again;
  • is sent as a bearer token on every request: Authorization: Bearer <token>.

API-key capability

Public API keys have one capability:

CapabilityGrants
telemetry:writeEmit telemetry; present on every key.
runtimesRead recipes and runtimes, manage experiments, open runners, and drive tasks, files, shares, conversations, events, and metrics within the key’s project and environment.

telemetry:write is always granted. remotes is not an API-key capability: it is reserved for the first-party CLI login used by introspection dev.

CLI login

introspection login uses the OAuth device flow and stores a member-bound session at ~/.introspection/credentials.json. The server, not the command line, chooses the granted CLI capabilities: runtimes for platform/operator commands and remotes for introspection dev. The CLI does not accept a capability selector, and applications or API keys cannot request the CLI-only remotes scope.

The CLI refreshes its login transparently and preserves the server-issued scope. See CLI for the login and profile workflow.

Configuring the client

The SDK reads its credentials and target URL from the environment by default. Set INTROSPECTION_TOKEN and the client picks it up:

export INTROSPECTION_TOKEN="ik_..."
import { IntrospectionClient } from "@introspection-sdk/introspection-node"; // Reads INTROSPECTION_TOKEN from the environment. const client = new IntrospectionClient(); // ...or pass the token explicitly. const explicit = new IntrospectionClient({ token: process.env.INTROSPECTION_TOKEN, });

The base API URL defaults to https://api.introspection.dev. Override it with INTROSPECTION_BASE_API_URL (for example to point at a different deployment).

Environment variables

VariableDefaultPurpose
INTROSPECTION_TOKENNoneThe SDK API key, sent as Authorization: Bearer <token>. Required for SDK platform calls unless a local development session supplies credentials.
INTROSPECTION_BASE_API_URLhttps://api.introspection.devBase URL for the platform API.

Server & machine auth

A long-lived INTROSPECTION_TOKEN is the simplest credential. For headless or CI callers that shouldn’t ship a static key, authenticate as a confidential service account instead: the SDK mints a short-lived, project-scoped token via the OAuth client_credentials grant and wires it in, so the rest of the flow is unchanged.

import { IntrospectionClient } from "@introspection-sdk/introspection-node"; const client = await IntrospectionClient.fromServiceAccount({ clientId: process.env.INTRO_SA_CLIENT_ID, // confidential client id clientSecret: process.env.INTRO_SA_CLIENT_SECRET, // kept server-side projectId: process.env.INTRO_PROJECT_ID, // token is project-scoped }); const runner = await client.runtimes("support-agent").run();

The Python SDK mirrors this with IntrospectionClient.from_service_account(...) (and an awaitable AsyncIntrospectionClient.from_service_account(...) twin). The minted token isn’t auto-refreshed, so re-mint it once it expires.

Brokering a token to the browser

When your server is a token broker for a browser app, mint the token directly so you can also read dp_url, the Data Plane endpoint the platform resolves for the project, and resolve the runtime name to a concrete id server-side. Hand the browser { token, runtimeId, dpUrl } so it connects straight to the Data Plane and never calls the platform API:

import { IntrospectionClient, serviceAccountToken, } from "@introspection-sdk/introspection-node"; const { access_token, dp_url } = await serviceAccountToken({ clientId: process.env.INTRO_SA_CLIENT_ID, clientSecret: process.env.INTRO_SA_CLIENT_SECRET, projectId: process.env.INTRO_PROJECT_ID, }); const client = new IntrospectionClient({ token: access_token }); const runtime = await client.runtimes.resolve("support-agent"); // hand { access_token, runtime.id, dp_url } to the browser client

The Python equivalents are service_account_token(...) and client.runtimes.resolve(...) (await it on the async client).

Browser apps

Browser code must not carry an API key; anything shipped to the client is public. Instead, client apps use the separate @introspection-sdk/introspection-browser/api package and its IntrospectionApiClient, which authenticates with a cookie session.

The session is issued by a backend token broker: your server holds the API key or service-account secret, exchanges it for a scoped, short-lived browser session, and the browser client rides that session. The long-lived credential never leaves your backend.

import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api"; // Authenticates via the cookie session your backend broker established. const client = new IntrospectionApiClient();

Never put an API key in browser code, a mobile app, or anything you ship to an end-user. Use the browser SDK with a backend token broker instead.

Last updated on