ob_live_… or ob_test_…) is the credential an agent presents to the gateway. It is never a vendor credential and never an operator session token. See Virtual keys for what the key carries and how the gateway reads it.
Minting and revoking require the key.manage action (developer, admin, or owner). Listing requires workspace.read, which every role holds. A frozen workspace answers 409 on mint and revoke.
Key object
Returned by all three routes. The mint response addsplaintext.
string
required
Key id, prefixed
key_. This is the id, never the secret.string
required
ob_live or ob_test. The minted secret begins with this plus an underscore.string
required
active, revoked, or expired. Computed at read time: a key past expires_at reports expired whatever the stored column says, and a revoked key reports revoked regardless of expiry.string
required
The agent this key was minted for.
string
required
The last four characters of the plaintext. The only fragment of the secret control retains.
array
required
Model allowlist.
["*"] when unrestricted.array
required
Tool allowlist.
["*"] when unrestricted.string
required
full or progressive.integer | null
required
Monthly spend cap in micro-USD, or
null for no cap.integer | null
required
Requests-per-minute limit, or
null.integer | null
required
Tokens-per-minute limit, or
null.array
required
Surfaces this key may use. Defaults to
["mcp", "llm"].string | null
required
RFC 3339 UTC expiry, or
null.object
required
Free-form attributes carried into the key context the gateway evaluates. Defaults to
{}.string
required
RFC 3339 UTC.
string | null
required
RFC 3339 UTC when the key was revoked, otherwise
null.Mint a key
POST /api/v1/workspaces/{workspace_id}/keys
Requires key.manage. Returns 201 with the key object plus plaintext.
The mint and its audit row commit in the same transaction, so a key that exists always has a row naming who created it. No secret material reaches the audit detail — it carries agent_id, prefix, last4, tools_mode, and scopes.
Path parameters
string
required
Workspace id, prefixed
ws_.Body
string
required
1–128 characters. Names the agent this key belongs to.
string
ob_live or ob_test. Defaults to ob_live in a prod workspace and ob_test otherwise. A ob_test prefix is rejected for a prod workspace.array
Model allowlist. Omitted or
null stores ["*"].array
Tool allowlist. Omitted or
null stores ["*"].string
default:"full"
full or progressive.integer
Monthly spend cap in micro-USD.
integer
Requests per minute. Must be at least 0.
integer
Tokens per minute. Must be at least 0.
array
Omitted or
null stores ["mcp", "llm"].object
Free-form attributes. Omitted or
null stores {}.string
RFC 3339 timestamp. After it passes, the key reports
expired.string
required
The full secret. Present only in this response. Store it now.
Errors
After the commit, control writes the key context to the Redis hot path. If that write fails the response still succeeds: Postgres is the source of truth, and the gateway falls back to reading the key from control.
List keys
GET /api/v1/workspaces/{workspace_id}/keys
Requires workspace.read. Returns every key in the workspace, revoked and expired ones included.
Path parameters
string
required
Workspace id, prefixed
ws_.array
required
Key objects, without
plaintext.Revoke a key
POST /api/v1/workspaces/{workspace_id}/keys/{key_id}/revoke
Requires key.manage. Returns 200 with the updated key object.
Revocation is idempotent: revoking an already-revoked key leaves revoked_at unchanged and still answers 200. The key is resolved inside the tenant boundary — workspace_id is in the query predicate, not in a follow-up check — so another tenant’s row is never loaded.
Path parameters
string
required
Workspace id, prefixed
ws_.string
required
Key id, prefixed
key_.Errors
After the commit, control drops the key from the Redis hot path. A failed drop does not fail the request; the durable revocation stands and the gateway’s fallback read reflects it.