Authentication
Authenticate against the KLA Control Plane API with OAuth 2.0 sign-in for humans and client-credentials service accounts for machines.
Every call to the KLA Control Plane API is authenticated with a short-lived OAuth 2.0 bearer access token and scoped to a single tenant. The KLA Control Plane is a govern-in-place runtime safety, audit, and governance layer for enterprise AI agents; its API is how you register agents, evaluate policies, route approvals, and pull evidence. This page explains the two credential types, how to obtain a token, how to call the API, and how to keep credentials least-privilege and rotated.
Two credential types
KLA issues tokens through its identity provider, an OpenID Connect (OIDC) service. Which flow you use depends on who is calling.
| Interactive sign-in | Service account | |
|---|---|---|
| Caller | A human in the Console (the KLA web app) | A backend service, script, or CI job |
| Flow | OAuth 2.0 / OIDC with PKCE | OAuth 2.0 client credentials |
| Secret | None stored by the app | client_id + client_secret |
| Identity | A person, with their roles | A machine principal you create |
| Use for | Reviewing the Decision Desk, building policies | SDK and API calls, automation |
Interactive sign-in is what the Console uses. The browser runs the OAuth 2.0 Authorization Code flow with PKCE (Proof Key for Code Exchange), so no client secret ever lives in front-end code. You do not implement this yourself; it ships with the Console.
Service accounts are what your integrations use. You create a service-account client per integration, give it the minimum roles it needs, and exchange its client_id and client_secret for an access token using the client-credentials grant.
flowchart LR H["Human in Console"] -->|"PKCE sign-in"| IDP["KLA identity provider"] M["Backend service"] -->|"client credentials"| IDP IDP -->|"bearer token"| API["KLA Control Plane API"]
Getting a service-account token
Exchange your client credentials at the identity provider's token endpoint. The realm path is your tenant, so substitute your tenant slug for <tenant>.
curl -s -X POST \
"https://auth.kla.digital/realms/<tenant>/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=svc-claims-triage" \
-d "client_secret=$KLA_CLIENT_SECRET"
The response is a JSON object containing the bearer access token and its lifetime in seconds:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"expires_in": 300,
"token_type": "Bearer",
"scope": "agents:read decisions:write"
}
Capture the token for the calls that follow:
export KLA_ACCESS_TOKEN=$(curl -s -X POST \
"https://auth.kla.digital/realms/<tenant>/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=svc-claims-triage" \
-d "client_secret=$KLA_CLIENT_SECRET" | jq -r .access_token)
Calling the API
Send the token in the Authorization header and name your tenant in the x-tenant-id header. The API host is https://api.kla.digital. Use this round-trip to confirm your token works:
curl -s https://api.kla.digital/v1/tenants.current \
-H "Authorization: Bearer $KLA_ACCESS_TOKEN" \
-H "x-tenant-id: <tenant>"
A successful call returns the tenant the token is scoped to:
{
"tenant": { "id": "acme", "name": "Acme Corp", "region": "eu" }
}
The sequence below shows the full path a machine client follows on each token cycle.
sequenceDiagram participant C as Client service participant IDP as KLA identity provider participant API as KLA Control Plane API C->>IDP: POST token endpoint with client credentials IDP-->>C: access_token plus expires_in C->>API: GET v1/tenants.current with bearer plus x-tenant-id API-->>C: 200 tenant payload Note over C,IDP: On 401 expired, request a fresh token and retry
Token lifetime and refresh
Access tokens are short-lived, typically five minutes (expires_in: 300). This limits the blast radius of a leaked token. Do not pin or cache a token past its expiry.
- Service accounts simply request a new token from the token endpoint when the current one nears expiry. The client-credentials grant does not issue a refresh token; re-running the exchange is the refresh.
- Interactive Console sessions use a separate, longer-lived refresh token managed by the browser to obtain new access tokens silently.
- A
401 Unauthorizedfrom the API means the token is expired or invalid. Request a fresh token and retry once.
Least-privilege scopes and roles
Each token carries the roles of its principal, and the API authorizes every request against them. Grant a service account only the roles its job requires:
- An agent that emits telemetry needs write access to traces, not policy authoring.
- An approvals automation needs
decisions:readanddecisions:write, not agent deployment rights. - A read-only evidence exporter needs evidence read access and nothing else.
Create one service account per integration so you can revoke or re-scope a single caller without disrupting others. Roles are managed in the Agent Registry and tenant settings of the Console.
Rotating service-account secrets
A client_secret is a long-lived credential: treat it like a database password. Rotate it on a schedule and immediately on any suspected exposure.
Store and rotate service-account secrets in the Secrets Vault, the Console surface for sensitive storage. Generate a new secret there, deploy it to your integration, confirm new tokens mint correctly, then retire the old secret. The Secrets Vault supports overlapping secrets during a rotation so you can roll forward with zero downtime.
