API referenceidentity
Mint api key
POST
/v1/keysAccepts bearer
Mint a scoped API key — the secret is in this response and nowhere else, ever
Requires keys:manage (session only, as built — an API key is 403 ACCESS_TOKEN_REQUIRED).Show more
Requires keys:manage (session only, as built — an API key is 403 ACCESS_TOKEN_REQUIRED). The key's scopes must be a subset of the minting user's; the key is a service principal that inherits nothing from the user who minted it. Stored hashed: a lost key is revoked and re-minted, never recovered. A key can also be exchanged for a short-lived Bearer token at POST /v1/oauth/token. A system key can never file an agent proposal (POST /v1/agent-proposals, AGENT_CREDENTIAL_REQUIRED). suppliers (optional) narrows the key to a supplier allowlist on top of its scopes — see MintApiKeyRequest.suppliers for the validation and the runtime answers. It is fixed at mint and survives rotation; console sessions are never narrowed by it. The request takes the key's scope — writes (KeyWrites), products, spendPerDay, lookToBook, reqPerSec, holdsPerHour, cancelsPerDay, expiresAt — and onBehalfOf, the Member the key acts for (default the caller; its principal and principalUserId). Minted by a session, the key is kind own and a member the request leaves out or sends null sets no key cap (the tenant's limits still govern), unchanged for people; a session's onBehalfOf naming a Member other than the caller needs users:manage (else 403 PERMISSION_DENIED details.reason MISSING_PERMISSION). The key records the agent of the X-Agent header. Either actorKind may mint: the device sign-up hands the agent a system key. A cap the request leaves out or sends null is the parent's cap, never null where the parent has one; the child keeps the parent's actorKind unless it asks for ai under a system parent, and an ai parent asking for system is widening (dimension actorKind). Anything wider is 403 PERMISSION_DENIED details.reason SCOPE_WIDENING with details.dimensions naming each wider member. The child is kind agt, mintedBy the parent's keyPrefix, and its principal is the parent's, fixed at mint: an onBehalfOf naming anyone else is 403 PERMISSION_DENIED details.reason PRINCIPAL_UNKNOWN, as is an onBehalfOf that is not an active Member of the tenant. A child never widens its ancestors' blast radius: every call of a minted key also counts against each ancestor key's velocity and spend windows, and while an ancestor is PAUSED its descendants answer 423 KEY_PAUSED (resuming the ancestor resumes them), so minting N children never multiplies what one key may do. Emits key.minted. MCP: the tool mint_api_key, on the same rule.Request body
- actorKindstringOptionalDefault "ai"
Anyone else asking for system is 403 PERMISSION_DENIED details.reason SYSTEM_KEY_REQUIRES_ADMIN.Show more
Anyone else asking for system is 403 PERMISSION_DENIED details.reason SYSTEM_KEY_REQUIRES_ADMIN. Any other value is a 400. The mint is audited with the actorKind.- ai
- system
- namestringOptionalA name for the key, shown in the key list.
- scopesarray of stringRequiredAt least one and at most 32 (ApiKeyService.MAX_SCOPES; more is 400 VALIDATION details.field scopes); known permission strings, a subset of the minting user's (deny-by-default)
- ipAllowlistarray of stringOptionalThe only addresses or ranges the key may call from; empty means any.
- suppliersarray of stringOptional
Optional supplier allowlist: the supplier registry codes (GET /v1/capabilities products[].suppliers[].code) this key may reach.Show more
Optional supplier allowlist: the supplier registry codes (GET /v1/capabilities products[].suppliers[].code) this key may reach. Absent or empty means every supplier the scopes reach. When set: every code must be a known supplier (else 400 VALIDATION details.field suppliers, details.reason UNKNOWN_SUPPLIER); every supplier must serve a product one of the key's search:<tabKey> scopes reaches (else 400 VALIDATION details.reason SUPPLIER_OUTSIDE_SCOPES); and every search:<tabKey> scope must keep at least one allowed supplier (else 400 VALIDATION details.reason SCOPE_WITHOUT_SUPPLIER). A search fans out to the allowed suppliers only; any other verb on an offer, reprice, hold or order of a supplier outside the list is 403 PERMISSION_DENIED details.reason SUPPLIER_NOT_ALLOWED_FOR_KEY, and GET /v1/capabilities lists the allowed suppliers only. - environmentstringOptional
Optional; defaults to this deployment's environment, and any other value is a 400.Show more
Optional; defaults to this deployment's environment, and any other value is a 400. A live key is minted only for a tenant whose production activation has been approved and never for a sandbox twin; otherwise 403 PERMISSION_DENIED, details.reason TENANT_NOT_LIVE. A SUSPENDED tenant mints nothing: 403 TENANT_SUSPENDED.- sandbox
- live
- writesone of 2Optional
The write ladder (KeyWrites); under the narrowing rule no higher than the minting key's, and the minting key's when left out or null.Show more
The write ladder (KeyWrites); under the narrowing rule no higher than the minting key's, and the minting key's when left out or null. Every cap below follows the same rule on a key mint; on a session mint left out or null is no key cap. - productsarray of stringOptionalProduct tab keys (hotels, flights), each reached by one of the key's search:<tabKey> scopes (else 400 VALIDATION details.field products).
- spendPerDayone of 2OptionalDaily spend cap, in the tenant's cap currency (else 400 VALIDATION CURRENCY_MISMATCH).
- lookToBookintegerOptionalThe most searches the key may make per booking, counted per UTC day; null means no cap.
- reqPerSecintegerOptionalThe most requests per second the key may make; null means no cap of its own.
- holdsPerHourintegerOptionalThe most holds the key may place per hour; null means no cap.
- cancelsPerDayintegerOptionalThe most cancellations the key may request per UTC day; null means no cap.
- expiresAtstring · date-timeOptionalIn the future (else 400 VALIDATION details.field expiresAt).
- onBehalfOfstring · emailOptional
The Member the key acts for (its principal); default the caller.Show more
The Member the key acts for (its principal); default the caller. A session may name itself, or another active Member of the tenant when it holds users:manage (else 403 PERMISSION_DENIED details.reason MISSING_PERMISSION); a key may name only its own stamped principal. Anyone else, or an address that is not an active Member, is 403 PERMISSION_DENIED details.reason PRINCIPAL_UNKNOWN.
Responses
- 201Minted. Show apiKey once; it cannot be retrieved again.MintedApiKey
- 400VALIDATION or UNKNOWN_DOMAINApiErrorResponse
- 401ApiErrorResponse
UNAUTHENTICATED — missing or expired token.Show more
UNAUTHENTICATED — missing or expired token. Declared on every operation because `security` is declared globally at the document root: every route is authenticated, so every route can answer 401. - 403ApiErrorResponse
The 403s of the credential-issuing routes (POST /v1/keys, POST /v1/keys/{keyPrefix}/rotate, POST /v1/invites).Show more
The 403s of the credential-issuing routes (POST /v1/keys, POST /v1/keys/{keyPrefix}/rotate, POST /v1/invites). PERMISSION_DENIED when the caller lacks keys:manage / users:manage, and on the key routes also when a live key is asked for a tenant that is not LIVE or is a sandbox twin. On POST /v1/keys also PERMISSION_DENIED with details.reason SYSTEM_KEY_REQUIRES_ADMIN when actorKind is system and the caller is not a tenant admin. - 405ApiErrorResponse
VALIDATION — the public edge serves GET, POST, PUT, PATCH and DELETE only; any other method is refused before the platform sees it (details.reason METHOD_NOT_ALLOWED, details.source edge).Show more
VALIDATION — the public edge serves GET, POST, PUT, PATCH and DELETE only; any other method is refused before the platform sees it (details.reason METHOD_NOT_ALLOWED, details.source edge). Not retryable as sent. - 413ApiErrorResponse
VALIDATION — the request body is larger than the surface's limit.Show more
VALIDATION — the request body is larger than the surface's limit. Not retryable as sent: shrink the body. Declared on every operation that takes a request body. details carries exactly one key, maxBodyBytes (integer): the limit that applies to this surface, in bytes. - 414ApiErrorResponse
VALIDATION — the request URI (path and query string) is longer than 8 KiB; the public edge refuses it before the platform sees it (details.reason URI_TOO_LONG, details.source edge).Show more
VALIDATION — the request URI (path and query string) is longer than 8 KiB; the public edge refuses it before the platform sees it (details.reason URI_TOO_LONG, details.source edge). Not retryable as sent: send long input in the body of a POST. - 415ApiErrorResponse
VALIDATION — a request body that is not JSON (application/json or application/*+json; a form body only on the OAuth token routes).Show more
VALIDATION — a request body that is not JSON (application/json or application/*+json; a form body only on the OAuth token routes). The public edge refuses it before the platform sees it (details.reason UNSUPPORTED_MEDIA_TYPE, details.source edge). Not retryable as sent: send Content-Type application/json. Declared on every operation that takes a JSON request body. - 423ApiErrorResponse
PERMISSION_DENIED with details.reason KEY_PAUSED: the API key is PAUSED and every call with it is refused until an Owner or Admin resumes it (POST /v1/keys/{keyPrefix}:resume, a person only); or…Show more
PERMISSION_DENIED with details.reason KEY_PAUSED: the API key is PAUSED and every call with it is refused until an Owner or Admin resumes it (POST /v1/keys/{keyPrefix}:resume, a person only); or details.reason LOOP_DETECTED on the call whose loop paused the key (20 identical requests in 60 s, or 50 holds without a booking or release in 10 min; replays of one Idempotency-Key never count). Not retryable; nextAction stop — the agent stops using the key and tells the person; details carry resumeBy (owner), consoleLink and evidence (ApiKeyPauseEvidence). Nothing was charged, held or sent; holds and orders already made are untouched. A key whose ancestor (the key that minted it, or that key's minter) is PAUSED answers the same 423 KEY_PAUSED, and resuming the ancestor resumes it, so a child never outlasts a pause of its parent. The MCP plane answers the identical body as an isError result. Any key-authenticated route may answer it; it is declared on the order and hold writes, search, reprice and the key routes. - 429ApiErrorResponse
RATE_LIMITED with details.reason VELOCITY_LIMIT: a soft breach of the API key's own scope (reqPerSec, holdsPerHour, cancelsPerDay, lookToBook, spendPerDay) — the first two in 24 hours; the third, or a…Show more
RATE_LIMITED with details.reason VELOCITY_LIMIT: a soft breach of the API key's own scope (reqPerSec, holdsPerHour, cancelsPerDay, lookToBook, spendPerDay) — the first two in 24 hours; the third, or a burst above 5× the limit, pauses the key instead (423 KeyPaused). Retryable after retryAfterMs (mirrored by Retry-After); nextAction slow_down; details carry limit, windowSeconds and the dimension in details.field. The tenant quota of RateLimited and the key's velocity are separate limits. Every call of a minted key also counts against each ancestor key's velocity and spend windows, so minting N children never multiplies the blast radius; spendPerDay is reserved atomically before the order is placed and released if it fails. - 500INTERNAL — a bug, and traceId is the support handle.ApiErrorResponse
- 501NOT_IMPLEMENTED AGENT_KEYS_UNAVAILABLE — a scope member was sent while scoped agent keys are offApiErrorResponse
Errors you should handle
Branch on code and honour retryable — never on the HTTP status alone.
| 400 | VALIDATION message.intent.checkOut must be after message.intent.checkIn | not retryable |
| 400 | UNKNOWN_DOMAIN Unknown domain 'HOTELS'. Did you mean HTL? | not retryable |
| 401 | UNAUTHENTICATED The bearer token is expired. Re-authenticate and retry. | not retryable |
| 403 | PERMISSION_DENIED Inviting colleagues needs the users:manage permission. | not retryable |
| 403 | TENANT_SUSPENDED This tenant is suspended; no key can be minted or rotated until it is reinstated. Existing keys can still be revoked. | not retryable |
| 405 | VALIDATION This method is not served. | not retryable |
| 413 | VALIDATION The request body is larger than this API accepts (262144 bytes). | not retryable |
| 414 | VALIDATION The request URI is longer than 8 KiB. | not retryable |
| 415 | VALIDATION Send application/json. | not retryable |
| 423 | PERMISSION_DENIED This API key is paused: 212 holds in 9 min. An Owner or Admin must resume it in the console. | not retryable |
| 429 | RATE_LIMITED This API key exceeded its rate: 10 requests a second. Retry after 1.2 s. | retryable |
| 500 | INTERNAL An unexpected error occurred. Quote the traceId to support. | retryable |
| 501 | NOT_IMPLEMENTED Key scopes beyond scopes and suppliers are not available on this deployment. | not retryable |