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.
| Field | Example values | Why it matters |
|---|---|---|
purpose | general, gateway_runtime, integration | Describes the workload class the token is meant to serve |
principal_type | api_token, gateway_runtime | Tells the policy engine which principal shape to resolve |
bindings | gateway_id, team_id, user_id, provider, model_filter, budget_id | Narrows what the token may access or where it may run, and defines default owner attribution |
token_value | kt_... | 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
| Workload | ABAC profile | Use it for |
|---|---|---|
App, SDK, IDE, or curl calling the gateway | purpose=general, principal_type=api_token | Governed chat, responses, embeddings, and other gateway-routed traffic |
| Hosted gateway runtime identity created with the CLI | purpose=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 integration | purpose=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.
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 class | Best fit | Required limits | What operators can inspect later |
|---|---|---|---|
durable | Long-lived gateway or client workloads | Optional expiry | Stable metadata, restrictions, and rotation/revoke controls |
virtual | Workloads that need tighter provider/model/gateway scoping without a second credential technology | Optional expiry | resource_id, resource_krn, attached policies, and live restriction summaries |
disposable | Short-lived execution windows and limited-use automation | Expiry plus budget or request depletion | Depletion 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_budgetormax_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 state | What it means | Safe next action |
|---|---|---|
| Budget near exhaustion | Spend crossed a configured warning threshold | Inspect recent usage, then rotate, clone with narrower scope, or revoke if the workload is done |
| Budget exhausted | The token has no remaining spend headroom | Treat new gateway requests as blocked; inspect budget context before creating a replacement |
| Request near exhaustion | Request count crossed a configured warning threshold | Check the caller and expected volume before extending the workload |
| Request exhausted | The token reached its request limit | Treat new gateway requests as blocked; rotate or clone only when the workload still needs access |
| Expiring | The token is inside its expiry warning window | Rotate or clone with narrower scope before the deadline |
| Expired | The expiry passed | Create a replacement only if the workload still needs access |
| Rotation due | Rotation policy says the token is stale | Rotate and update the consuming workload |
| Emergency revoked | An operator revoked immediately with a reason | Investigate 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_filterfrom 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:
- capture the operator reason
- revoke the token immediately
- invalidate gateway validation/cache state for the token
- block future API validation, CLI validation, and gateway use
- 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
- Open API Tokens (
/tokens). - Select Create token.
- Enter a name, choose the key class (
durable,virtual, ordisposable), and optionally set an expiry. - Select at least one role. The console requires a role before it enables Create token; access policies are optional additional restrictions.
- 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_idis optional, but when bothteam_idanduser_idare present the user must belong to that team.
- Copy the token immediately. The raw
token_valueis 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.
| Binding | What it narrows |
|---|---|
gateway_id | Restricts the token to a specific hosted gateway runtime |
team_id | Attributes or narrows the token to a specific team context |
user_id | Attributes the token to a specific user and becomes the default spend owner when the gateway omits explicit user attribution |
provider | Restricts upstream provider access |
model_filter | Limits which models the token may route to |
budget_id / max_budget | Constrains 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
- Open API Tokens (
/tokens). - Select the token name to open its detail page.
- 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
| Practice | Why |
|---|---|
| Choose the token by ABAC profile, not by label | Reduces confusion between app traffic and runtime identity |
Use purpose=general for app-side gateway requests | Keeps browser-safe and script-safe traffic on the intended lane |
Reserve purpose=gateway_runtime for KEEPTRUSTS_API_TOKEN on the gateway process | Prevents accidental privilege bleed into clients |
| Add bindings early | Narrows blast radius before the token is ever used |
| Set expiry on long-lived tokens | Limits damage from leaked credentials |
| Use disposable keys for bounded automation | Gives short-lived workloads automatic stop conditions |
| Treat exhaustion and expiry as fail-closed signals | Prevents accidental provider execution after a token is depleted |
| Match expiry and rotation to your organization's credential policy | Keeps token lifetime aligned with the workload and risk level |
| Never commit tokens to version control | Use environment variables or secret managers |
Monitor last_used_at and revoke stale tokens | Removes forgotten credentials before they become risk |
Next steps
- Install the Gateway — configure runtime tokens for a local or shared gateway
- Debugging AI Requests — inspect governed request outcomes and live decision signals