Skip to Content
Platform
SDKs & CLIAuthentication

Authentication

Configure the credential flow your server, browser application, or CLI uses after choosing the right identity trust model.

First choose the right trust model in Authentication & identity: an API key, a service account that supplies the user’s identity, or federation that proves the identity through your IdP. Then use the matching setup below.

API key

Set the Introspection project API key as INTROSPECTION_TOKEN. The server-side SDK reads it automatically:

export INTROSPECTION_TOKEN="intro_..."
import { IntrospectionClient } from "@introspection-sdk/introspection-node"; const client = new IntrospectionClient(); // Or pass it 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 when targeting another deployment.

Each API key:

  • belongs to one Introspection project and environment;
  • carries runtimes, the platform’s sole API-key capability (there is no capability selector), and the same token authenticates the OTLP exporters;
  • is shown once and stored only as a hash;
  • remains valid until it expires or is revoked.

Never put an API key in browser code, a mobile application, or an agent sandbox. Anything shipped to an end user must be treated as public.

Service-account application

A confidential service-account application exchanges its client ID and secret for a short-lived token through OAuth client_credentials:

import { IntrospectionClient } from "@introspection-sdk/introspection-node"; const client = await IntrospectionClient.fromServiceAccount({ clientId: process.env.INTROSPECTION_SERVICE_ACCOUNT_CLIENT_ID, clientSecret: process.env.INTROSPECTION_SERVICE_ACCOUNT_CLIENT_SECRET, project: process.env.INTROSPECTION_PROJECT, });

The token is scoped to the selected Introspection project and capped to the application’s allowed scopes. No refresh token is issued; mint another access token when it expires. Python provides IntrospectionClient.from_service_account(...) and AsyncIntrospectionClient.from_service_account(...).

Pass an application user identity

The service account authenticates your backend. To run work for one of your application’s users, pass that user’s stable ID when you open the runner:

const runner = await client.runtimes("support-agent").run({ identity: { user_id: applicationUser.id }, });

This is caller-attributed identity: your authenticated application vouches for the supplied user_id. It is a normal B2B2C and browser-client flow, not a machine-only mode. Use a federated application instead when Introspection must verify the end user through your IdP.

Federated application

For a federated application, your backend exchanges the end user’s signed IdP token through RFC 8693 token exchange:

import { IntrospectionClient, tokenExchange, } from "@introspection-sdk/introspection-node"; const { access_token, dp_url } = await tokenExchange({ subjectToken: idTokenFromYourIdp, clientId: process.env.INTROSPECTION_FEDERATED_CLIENT_ID, project: process.env.INTROSPECTION_PROJECT, }); const client = new IntrospectionClient({ token: access_token });

The application configuration identifies the trusted issuer and JWKS. Introspection verifies the token before minting an access token for the corresponding customer member. The resulting user identity is federation-proven rather than supplied by application code.

Run token exchange in your backend. Do not retain or repeatedly move the user’s IdP token through browser code beyond the login flow.

Browser token broker

Browser applications use @introspection-sdk/introspection-browser/api. They do not hold an API key or service-account secret and never call the Control Plane directly.

Your backend broker:

  1. authenticates the application user;
  2. resolves the runtime through the Control Plane;
  3. establishes an identity-bound runner using a service account, or mints an end-user token through federated token exchange or hosted login;
  4. returns { token, runtimeId, dpUrl } with Cache-Control: no-store.

For a service-account broker:

import { IntrospectionClient, } from "@introspection-sdk/introspection-node"; const controlPlane = await IntrospectionClient.fromServiceAccount({ clientId: process.env.INTROSPECTION_SERVICE_ACCOUNT_CLIENT_ID, clientSecret: process.env.INTROSPECTION_SERVICE_ACCOUNT_CLIENT_SECRET, project: process.env.INTROSPECTION_PROJECT, }); const runtime = await controlPlane.runtimes.resolve("support-agent"); const session = await controlPlane.runtimes.openRunner(runtime.id, { // `applicationUser` came from your own authenticated browser session. identity: { user_id: applicationUser.id }, }); // Return this with Cache-Control: no-store. return { token: session.session_token, runtimeId: runtime.id, dpUrl: session.deployment.endpoint, };

The important step is openRunner(..., { identity }): the browser receives a narrow runner session already bound to the application user. The general service-account access token and client secret never reach the browser. With federated or hosted login, the exchanged token establishes the end-user identity instead.

The browser supplies a getToken callback and exchanges the short-lived token for an HttpOnly Data Plane session cookie:

import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api"; async function brokerSession() { const response = await fetch("/api/introspection/session", { method: "POST", cache: "no-store", }); if (!response.ok) throw new Error("Could not establish an agent session"); return response.json(); } const initial = await brokerSession(); let initialToken: string | null = initial.token; const client = new IntrospectionApiClient({ dpUrl: initial.dpUrl, getToken: async () => { if (initialToken) { const token = initialToken; initialToken = null; return token; } return (await brokerSession()).token; }, }); await client.connect(); const run = await client.tasks.start({ runtime_id: initial.runtimeId, prompt: "Summarize my open tickets", }); for await (const event of run.stream()) console.log(event);

connect() exchanges the token for a secure browser session. The session is kept separate for development, staging, and production, so one environment does not replace another. Task, file, conversation, and share calls use the session automatically. When it expires, the SDK can use getToken to refresh it and retry the request once.

The browser only calls the selected Data Plane, so the Control Plane does not need to allow the application’s web origin.

Hosted browser login

A public SPA application can use Introspection-hosted login with authorization code + PKCE. The public client has no secret. After the redirect, send the authorization code, original redirect URI, and PKCE verifier to your backend and exchange them with authorizationCodeToken(...).

Hosted login is member authentication. It is separate from service-account identity attribution and partner-IdP federation.

CLI login

introspection login uses a browser-approved device flow and stores a member-bound session at ~/.introspection/credentials.json. The server chooses the granted capabilities: runtimes for platform operations and remotes for introspection dev.

The CLI refreshes its login transparently. Applications and API keys cannot request the CLI-only remotes capability. See CLI for the login and profile workflow.

Last updated on