Skip to main content

Managing API Tokens

Keeptrusts uses one API-token system. What makes one token behave differently from another is the ABAC context attached to it: purpose, principal_type, and any runtime bindings such as gateway_id, team_id, user_id, provider filters, model filters, or budget limits.

The important distinction is not "which kind of key product did I create?" It is "which policy profile did I mint into this API token?"

The ABAC model

Every Keeptrusts API token is a kt_... secret. The ABAC layer decides what that token may do.

FieldExample valuesWhy it matters
purposegeneral, gateway_runtime, integrationDescribes the workload class the token is meant to serve
principal_typeapi_token, gateway_runtimeTells the policy engine which principal shape to resolve
bindingsgateway_id, team_id, user_id, provider, model_filter, budget_idNarrows what the token may access or where it may run, and defines default owner attribution
token_valuekt_...The one-time secret sent as Authorization: Bearer ...

CLI flags use kebab-case for multiword purpose values, so kt token create and kt token list use gateway-runtime even though API payloads and token metadata use gateway_runtime.

Common token profiles

WorkloadABAC profileUse it for
App, SDK, IDE, or curl calling the gatewaypurpose=general, principal_type=api_tokenGoverned chat, responses, embeddings, and other gateway-routed traffic
Hosted gateway runtime identity created with the CLIpurpose=gateway_runtime, principal_type=api_token, bindings.gateway_id=<gateway-id>, bindings.team_id=<team-id>KEEPTRUSTS_API_TOKEN on the gateway process itself
Long-lived machine integrationpurpose=integration, principal_type=api_token, bindings.team_id=<team-id>Service-to-service jobs that need explicit machine identity outside the browser-safe gateway lane

kt token create sends the purpose and bindings but does not expose a principal_type flag, so the API default remains api_token. Advanced API callers may explicitly create principal_type=gateway_runtime; that machine-principal form also requires a team binding.

tip

Older docs may call the first profile an "access key" and the second a "gateway key." Keeptrusts now treats both as API tokens with different ABAC profiles.

Governed gateway keys

Gateway-facing API tokens can also be issued as governed key classes.

Key classBest fitRequired limitsWhat operators can inspect later
durableLong-lived gateway or client workloadsOptional expiryStable metadata, restrictions, and rotation/revoke controls
virtualWorkloads that need tighter provider/model/gateway scoping without a second credential technologyOptional expiryresource_id, resource_krn, attached policies, and live restriction summaries
disposableShort-lived execution windows and limited-use automationExpiry plus budget or request depletionDepletion counters, expiry, and automatic denial after exhaustion

Every governed gateway key still lives in the unified /v1/tokens inventory. Create and auth-code exchange responses reveal token_value once. Later list/read/inspect flows show metadata only, including key_class, resource_id, resource_krn, restrictions, and depletion state.

Disposable token lifecycle operations

Disposable governed keys are for short-lived execution windows. Use them when a workload should stop automatically after a time, spend, or request boundary rather than relying only on manual cleanup.

Disposable keys require:

  • an expiry such as expires_in_seconds
  • at least one depletion control, such as max_budget or max_requests
  • narrow bindings for gateway, provider, model, team, or policy where possible

Lifecycle alerts for disposable keys resolve to API Tokens at /tokens. Overview, Inbox, and Usage/Budgets may deep-link to the same token detail view, but /tokens remains the action surface.

Lifecycle stateWhat it meansSafe next action
Budget near exhaustionSpend crossed a configured warning thresholdInspect recent usage, then rotate, clone with narrower scope, or revoke if the workload is done
Budget exhaustedThe token has no remaining spend headroomTreat new gateway requests as blocked; inspect budget context before creating a replacement
Request near exhaustionRequest count crossed a configured warning thresholdCheck the caller and expected volume before extending the workload
Request exhaustedThe token reached its request limitTreat new gateway requests as blocked; rotate or clone only when the workload still needs access
ExpiringThe token is inside its expiry warning windowRotate or clone with narrower scope before the deadline
ExpiredThe expiry passedCreate a replacement only if the workload still needs access
Rotation dueRotation policy says the token is staleRotate and update the consuming workload
Emergency revokedAn operator revoked immediately with a reasonInvestigate the proof trail and replace only after the incident is understood

Budget-exhausted, request-exhausted, expired, rotated, and revoked disposable keys fail closed before provider execution. Denial messages identify the token resource and reason without exposing the raw token value.

Clone with narrower scope

Use clone-with-narrower-scope when the workload still needs access but the existing token is too broad or near a limit.

The cloned token must be equal to or narrower than the source token. The API rejects any attempt to widen provider, model, gateway, policy, budget, request, rate, expiry, team, or metadata trust boundaries.

Examples of allowed narrowing:

  • reduce model_filter from two models to one
  • reduce max_budget
  • reduce max_requests
  • shorten expires_at
  • bind to a narrower gateway or team when the source token allowed that scope

The new token value is shown once in the clone response. The source token, alerts, work items, list views, detail views, and proof events never show the raw secret.

Emergency revoke

Use emergency revoke when a disposable token may be exposed, misused, or no longer safe to keep active.

Emergency revoke should:

  1. capture the operator reason
  2. revoke the token immediately
  3. invalidate gateway validation/cache state for the token
  4. block future API validation, CLI validation, and gateway use
  5. create proof in Trail without exposing the token value

After emergency revoke, create a replacement only after confirming the new scope is safe.

Creating API tokens

Via the Console

  1. Open API Tokens (/tokens).
  2. Select Create token.
  3. Enter a name, choose the key class (durable, virtual, or disposable), and optionally set an expiry.
  4. Select at least one role. The console requires a role before it enables Create token; access policies are optional additional restrictions.
  5. Add any bindings you need, such as team or user attribution, Agent/gateway binding, provider and model filters, credit ceiling, or request ceiling.
    • For shared or service tokens, set a team owner.
    • user_id is optional, but when both team_id and user_id are present the user must belong to that team.
  6. Copy the token immediately. The raw token_value is shown once.

The console currently creates the general governed-token purpose. Create gateway-runtime and integration purpose tokens through the CLI or API when you need those machine-specific profiles.

Via the CLI

Create a gateway request token for apps and scripts:

kt token create \
--name "support-web-prod" \
--purpose general \
--expires-in 90d

Create a hosted gateway runtime token:

kt token create \
--name "hosted-gateway-eu-west-1" \
--purpose gateway-runtime \
--gateway-id 00000000-0000-4000-a000-000000000123 \
--team-id 00000000-0000-4000-a000-000000000456 \
--expires-in 30d

Create a scoped integration token:

kt token create \
--name "billing-export-job" \
--purpose integration \
--team-id 00000000-0000-4000-a000-000000000456 \
--expires-in 30d

Via the API

Create a gateway request token:

curl -X POST https://api.keeptrusts.com/v1/tokens \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "support-web-prod",
"purpose": "general",
"principal_type": "api_token",
"bindings": {
"team_id": "00000000-0000-4000-a000-000000000456",
"user_id": "00000000-0000-4000-a000-000000000789",
"model_filter": ["gpt-5.4-mini"]
},
"expires_in_seconds": 7776000
}'

Create a hosted gateway runtime token:

curl -X POST https://api.keeptrusts.com/v1/tokens \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "hosted-gateway-eu-west-1",
"purpose": "gateway_runtime",
"principal_type": "gateway_runtime",
"bindings": {
"gateway_id": "00000000-0000-4000-a000-000000000123",
"team_id": "00000000-0000-4000-a000-000000000456"
}
}'

Successful creation returns a one-time secret:

{
"id": "00000000-0000-4000-a000-000000000301",
"name": "support-web-prod",
"token_value": "kt_...",
"token_prefix": "kt_abcd",
"purpose": "general",
"principal_type": "api_token"
}

Using a token correctly

App or script calling the gateway

Use an API token with purpose=general:

curl https://gateway.example.com/v1/chat/completions \
-H "Authorization: Bearer $KEEPTRUSTS_GATEWAY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4-mini",
"messages": [
{ "role": "user", "content": "Summarize the incident." }
]
}'

Gateway runtime connecting to the control plane

Use a hosted-runtime API token with purpose=gateway_runtime as the gateway process credential:

export KEEPTRUSTS_API_URL="https://api.keeptrusts.com"
export KEEPTRUSTS_API_TOKEN="kt_..."

Do not reuse the gateway runtime token for browser apps, IDEs, or curl requests that are acting as ordinary gateway clients.

Scoping with bindings

Bindings are where the ABAC model becomes operational.

BindingWhat it narrows
gateway_idRestricts the token to a specific hosted gateway runtime
team_idAttributes or narrows the token to a specific team context
user_idAttributes the token to a specific user and becomes the default spend owner when the gateway omits explicit user attribution
providerRestricts upstream provider access
model_filterLimits which models the token may route to
budget_id / max_budgetConstrains spend at the token level

If a token lacks the binding or policy permission required for a request, the request is denied even though the raw secret is structurally valid.

The Usage explorer can drill down by token. That view uses the token binding owner fields as the default spend attribution when the gateway only forwards key_id.

Listing and inspecting tokens

List all tokens:

kt token list

Filter by purpose:

kt token list --purpose gateway-runtime

Inspect a specific token:

kt token get 00000000-0000-4000-a000-000000000301

API list filtering uses the same ABAC fields:

curl -H "Authorization: Bearer $ADMIN_TOKEN" \
"https://api.keeptrusts.com/v1/tokens?purpose=general&status=active"

Rotation

The safest production rotation pattern is still overlap-first:

# 1. Create the replacement token
kt token create \
--name "support-web-prod-next" \
--purpose general \
--expires-in 90d

# 2. Deploy it to the consuming workload

# 3. Send a probe with a 32-character lowercase-hex X-Request-Id
# Confirm the Event with that request_id attributes use to the replacement token.

# 4. Revoke the old token
kt token delete 00000000-0000-4000-a000-000000000301 --yes

Rotating a token does not change its ABAC profile. If you need a different purpose, principal_type, or broader binding set, mint a new token instead of rotating the old one.

Revocation

Via the Console

  1. Open API Tokens (/tokens).
  2. Select the token name to open its detail page.
  3. Open More ▾, select Revoke, and confirm. For a bulk operation, select token checkboxes in the inventory and use Revoke selected.

Revoked tokens are immediately invalid. Any in-flight requests using the token may finish, but new requests are rejected with 401 Unauthorized.

Via the CLI

kt token delete 00000000-0000-4000-a000-000000000301 --yes

Security best practices

PracticeWhy
Choose the token by ABAC profile, not by labelReduces confusion between app traffic and runtime identity
Use purpose=general for app-side gateway requestsKeeps browser-safe and script-safe traffic on the intended lane
Reserve purpose=gateway_runtime for KEEPTRUSTS_API_TOKEN on the gateway processPrevents accidental privilege bleed into clients
Add bindings earlyNarrows blast radius before the token is ever used
Set expiry on long-lived tokensLimits damage from leaked credentials
Use disposable keys for bounded automationGives short-lived workloads automatic stop conditions
Treat exhaustion and expiry as fail-closed signalsPrevents accidental provider execution after a token is depleted
Match expiry and rotation to your organization's credential policyKeeps token lifetime aligned with the workload and risk level
Never commit tokens to version controlUse environment variables or secret managers
Monitor last_used_at and revoke stale tokensRemoves forgotten credentials before they become risk

Next steps