Authentication
Wallet-backed, OpenAI- and Anthropic-compatible inference. Authenticate with Authorization: Bearer ci_live_... or x-api-key: ci_live_.... Both are accepted on every endpoint, and Authorization wins if both are present.
Every successful BUFFERED response carries request_id and billing at the top level, with the request id and what the request was billed. Messages streams carry them on the terminal message_delta, and Responses streams on the terminal response.completed or response.incomplete. Chat Completions and Completions streams carry neither: reconcile those from the x-ci-request-id response header. billed_cost_usd is a fixed 6-decimal string, not a number: a JSON float cannot round-trip a micro-dollar amount.
JSON inference bodies may be compressed: /v1/chat/completions, /v1/completions, /v1/responses, /v1/messages, /v1/messages/count_tokens, /v1/images/generations and /v1/videos/generations. Send Content-Encoding: gzip, deflate or br and the body is inflated before parsing, which is how a conversation larger than the 4.5 MB wire limit can be sent. A single encoding only, and a body that inflates past 20 MB is refused with 413 - except on /v1/messages/count_tokens, where the inflated ceiling is 4.5 MB. Multipart image edits and the non-inference endpoints read their bodies directly and expect them uncompressed.
This document covers the customer-facing /v1 surface only.
Base URL
https://api.cheaperinference.com
Version 1.0.0 of the document. Paths below are relative to this base.
- BearerAuth — HTTP bearer: send `Authorization: Bearer <key>`.
- AnthropicApiKey — API key in the `x-api-key` request header.
Both schemes are accepted on every endpoint, and Authorization wins if both are present.
Chat
OpenAI-compatible chat and text completion.
/v1/chat/completionsstreamsCreate a chat completion
OpenAI-compatible. The request is optimized, served from the exact-match cache when possible, and billed against the wallet. Two opt-in request headers form the client contract Keak Code uses: `x-ci-model-lock` (serve exactly the named model) and `x-ci-stream-receipt` (receipt frames in the stream).
Query parameters
| Field | Type | Notes |
|---|---|---|
x-ci-techniques | string | Set to off to disable optional workspace techniques for a controlled benchmark. Preserves workspace defaults and billing policy; takes precedence over technique opt-in headers.off |
x-ci-prompt-cache | string | Prompt-cache knob. `passthrough` (default, or the API key's `prompt_cache_mode`) forwards the request unchanged; `on` additionally adds provider cache breakpoints for supported models. For Claude, unmarked requests cache the shared system/tools prefix and growing conversation; caller-supplied cache controls and lifetimes are preserved. `off` removes explicit cache controls, disables sticky affinity for this request and, for models that read a cache marker, prefers routes that forward the body as sent over the marketplace; it does not disable a provider's implicit caching.on, off, passthrough |
x-ci-prompt-cache-scope | string | Sticky-affinity scope: `session` (default), `user`, or `org`. Scopes the affinity key that keeps a repeated prompt prefix on the same upstream provider so its cache stays warm.session, user, org |
x-ci-prompt-cache-session | string | Session id read when the scope is `session`. Send one stable value per conversation or agent run. When absent, a string prompt_cache_key in the request body is used. Without either identifier, the tools, opening system instructions and first conversation message identify the session. Affinity remains isolated to the API key. |
x-ci-model-lock | string | Model Lock. Serve exactly the model named in the body: no model-routing technique, no experiment variant and no admission canary may substitute it. Combined with `X-CI-Experiment` (or a forced experiment variant) it is refused with 400 `model_lock_conflict` before anything is read or reserved. If any stage would still change the model, the request is refused with 500 `model_lock_violation` before reaching a model and nothing is billed. Model routing is held off even when the key, the workspace or `x-ci-route` turns it on. `1` or `true` (any case) turns it on; absent or empty leaves it off. Any other value, including `0` and `false`, is refused with 400 `invalid_contract_header` naming the header in `param`, so a typo never silently means off.1, true |
x-ci-stream-receipt | string | Receipt mode. On a streamed `/v1/chat/completions` request (`stream: true`), add two standalone SSE frames: an opening frame before the first upstream frame and a final receipt after billing finalize, before `data: [DONE]`. See the `text/event-stream` response. Accepted and ignored on a non-streamed request. `1` or `true` (any case) turns it on; absent or empty leaves it off. Any other value, including `0` and `false`, is refused with 400 `invalid_contract_header` naming the header in `param`, so a typo never silently means off.1, true |
x-ci-vercel-provider | string | Opt-in strict Vercel upstream provider pin for operator-enabled model/provider pairs. Use the same provider ID on every request to retain provider consistency. Requires Core chat completions; fails rather than falling back to another source. Conflicts with API-key seller routing rules. Does not guarantee a cache hit or change the admitted customer price. Omit for normal routing. |
Request body ChatCompletionRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
messagesrequired | object[] | |
stream | boolean | |
max_tokens | integer | |
max_completion_tokens | integer | Alternative output-token limit. If both limits are supplied, they must match. |
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
temperature | number | Model-specific sampling control. Omit on models that do not support sampling. Claude Chat-to-native translation caps values above 1 at 1. |
top_p | number | Model-specific sampling control. Omit on models that do not support it. |
n | integer | Number of completions. Claude supports only 1. |
tools | object[] | |
tool_choice | any | |
reasoning_effort | string | Alias for reasoning.effort, including Claude. Conflicting values return 400. Supported levels depend on the model; Gemini 3 accepts minimal, low, medium, and high only. Claude native bridges translate effort into adaptive thinking or a manual budget within max_tokens. A manual budget must be an integer of at least 1024, strictly below the output limit. Thinking summaries, when returned, appear in message.reasoning_content or delta.reasoning_content. Native thinking and output_config settings take precedence.none, minimal, low, medium, high, xhigh, max |
reasoning | object | boolean | null | Portable reasoning controls, including Claude. false disables thinking where supported. Use /v1/responses for Pro. Chat Completions returns HTTP 400 for reasoning.mode: pro or an OpenAI Pro model ID such as gpt-5.5-pro. |
thinking | object | Native Claude thinking configuration. Takes precedence over portable effort. Active thinking supports auto or none tool_choice, not required or a forced named tool. |
output_config | object | Native Claude output configuration, including effort for adaptive thinking. Preserved by the native Chat bridges. |
include_reasoning | boolean | Claude Chat thinking summaries are hidden by default. Set true (or reasoning.exclude: false) to include them. False or reasoning.exclude: true always hides them without disabling thinking or changing token billing. |
cache_control | object | Request-level Claude cache control. Supported controls on tools, messages and content blocks are also preserved by native bridges. An opt-in does not guarantee a cache hit; off or ZDR removes explicit controls. |
Additional properties are forwarded to the serving provider rather than rejected.
Response ChatCompletionResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`) Under the client contract also `code: invalid_contract_header` (a bad `x-ci-model-lock` or `x-ci-stream-receipt` value) and `code: model_lock_conflict` (Model Lock combined with an experiment). While the key has an open task budget (`/v1/keys/self/task-budgets`), also `code: task_budget_exhausted` (`param: task_budget`): the request's worst-case charge does not fit what the budget has left, so it was refused before anything was held and nothing was charged. Only with a contract header, when the workspace policy requires a task budget and the key has none open, also `code: task_budget_required` (`param: task_budget`); nothing was held or charged. Only with a contract header, every 402, 403 and 429 refusal on the inference path is answered as this 400 instead, with one of these codes: `code: workspace_monthly_cap_hit` (`param: workspace_monthly_cap`), `code: member_monthly_cap_hit` (`param: member_monthly_cap`), `code: wallet_too_low` (`param: wallet`), `code: wallet_top_up_failed` (`param: wallet`), `code: key_monthly_cap_hit` (`param: key_monthly_cap`), `code: contract_funding_refused`, `code: workspace_membership_ended` (`param: workspace_membership`), `code: key_scope_missing` (`param`: the missing scope), `code: key_network_refused` (`param: key_network`), `code: key_allowlist_refused` (`param: key_allowlist`), `code: key_policy_refused` (`param: key_policy`), `code: provider_policy_block` (`param: end_user`), `code: contract_access_refused`, `code: key_minute_cap_hit` (`param: key_minute_cap`), `code: key_daily_cap_hit` (`param: key_daily_cap`), `code: key_parallel_cap_hit` (`param: key_parallel_cap`), `code: contract_pacing_refused`. Each message starts with a distinct phrase and nothing was charged unless it says otherwise. Without a contract header these refusals keep their usual status and code.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`) Under Model Lock also `code: model_lock_violation`: a stage would have served another model, so the request was refused before reaching any model and nothing was billed.502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/completionsstreamsCreate a text completion
OpenAI-compatible legacy completions.
Query parameters
| Field | Type | Notes |
|---|---|---|
x-ci-model-lock | string | Model Lock. Serve exactly the model named in the body: no model-routing technique, no experiment variant and no admission canary may substitute it. Combined with `X-CI-Experiment` (or a forced experiment variant) it is refused with 400 `model_lock_conflict` before anything is read or reserved. If any stage would still change the model, the request is refused with 500 `model_lock_violation` before reaching a model and nothing is billed. Model routing is held off even when the key, the workspace or `x-ci-route` turns it on. `1` or `true` (any case) turns it on; absent or empty leaves it off. Any other value, including `0` and `false`, is refused with 400 `invalid_contract_header` naming the header in `param`, so a typo never silently means off.1, true |
Request body CompletionRequest
| Field | Type | Notes |
|---|---|---|
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
modelrequired | string | |
promptrequired | string | array | |
stream | boolean | |
max_tokens | integer |
Additional properties are forwarded to the serving provider rather than rejected.
Response CompletionResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`) Under the client contract also `code: invalid_contract_header` (a bad `x-ci-model-lock` or `x-ci-stream-receipt` value) and `code: model_lock_conflict` (Model Lock combined with an experiment). While the key has an open task budget (`/v1/keys/self/task-budgets`), also `code: task_budget_exhausted` (`param: task_budget`): the request's worst-case charge does not fit what the budget has left, so it was refused before anything was held and nothing was charged. Only with a contract header, when the workspace policy requires a task budget and the key has none open, also `code: task_budget_required` (`param: task_budget`); nothing was held or charged. Only with a contract header, every 402, 403 and 429 refusal on the inference path is answered as this 400 instead, with one of these codes: `code: workspace_monthly_cap_hit` (`param: workspace_monthly_cap`), `code: member_monthly_cap_hit` (`param: member_monthly_cap`), `code: wallet_too_low` (`param: wallet`), `code: wallet_top_up_failed` (`param: wallet`), `code: key_monthly_cap_hit` (`param: key_monthly_cap`), `code: contract_funding_refused`, `code: workspace_membership_ended` (`param: workspace_membership`), `code: key_scope_missing` (`param`: the missing scope), `code: key_network_refused` (`param: key_network`), `code: key_allowlist_refused` (`param: key_allowlist`), `code: key_policy_refused` (`param: key_policy`), `code: provider_policy_block` (`param: end_user`), `code: contract_access_refused`, `code: key_minute_cap_hit` (`param: key_minute_cap`), `code: key_daily_cap_hit` (`param: key_daily_cap`), `code: key_parallel_cap_hit` (`param: key_parallel_cap`), `code: contract_pacing_refused`. Each message starts with a distinct phrase and nothing was charged unless it says otherwise. Without a contract header these refusals keep their usual status and code.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`) Under Model Lock also `code: model_lock_violation`: a stage would have served another model, so the request was refused before reaching any model and nothing was billed.502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Images
OpenAI-compatible image generation and canary-gated image editing.
/v1/images/generationsGenerate images
Generate one or more images. Streaming is not supported on this endpoint.
Request body ImageGenerationRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
promptrequired | string | length 1–∞ |
n | integer | 1–10 |
size | string | |
quality | string | |
response_format | string |
Additional properties are forwarded to the serving provider rather than rejected.
Response ImageResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/images/editsEdit images
Edit up to 10 source images with a 20 MiB decoded aggregate limit across the sources and optional mask. The OpenAI-compatible multipart form accepts `image` uploads and an optional PNG `mask`. The JSON extension accepts `input_references` containing HTTP(S) URLs or image data URLs; external URLs are passed only to compatible providers and are never fetched or stored by CheaperInference. Models are eligible only after a live edit canary; discover them through `capabilities.image_edit` on GET /v1/models.
Request body ImageEditJsonRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
promptrequired | string | length 1–32000 |
input_referencesrequired | string[] | |
mask_reference | string | Optional PNG data URL. External mask URLs are not accepted. |
n | integer | 1–10 |
size | string | |
quality | string | |
background | string | |
input_fidelity | string | |
output_format | string | |
output_compression | integer | 0–100 |
response_format | string | |
user | string | |
stream | boolean | false |
Response ImageResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Messages
Anthropic Messages. Claude Code integrates here.
/v1/messagesstreamsCreate a message
Anthropic Messages. `max_tokens` is required. Errors use Anthropic's envelope, which carries no `code` or `param`.
Query parameters
| Field | Type | Notes |
|---|---|---|
x-ci-techniques | string | Set to off to disable optional workspace techniques for a controlled benchmark. Preserves workspace defaults and billing policy; takes precedence over technique opt-in headers.off |
x-ci-prompt-cache | string | Prompt-cache knob. `passthrough` (default, or the API key's `prompt_cache_mode`) forwards the request unchanged; `on` additionally adds provider cache breakpoints for supported models. For Claude, unmarked requests cache the shared system/tools prefix and growing conversation; caller-supplied cache controls and lifetimes are preserved. `off` removes explicit cache controls, disables sticky affinity for this request and, for models that read a cache marker, prefers routes that forward the body as sent over the marketplace; it does not disable a provider's implicit caching.on, off, passthrough |
x-ci-prompt-cache-scope | string | Sticky-affinity scope: `session` (default), `user`, or `org`. Scopes the affinity key that keeps a repeated prompt prefix on the same upstream provider so its cache stays warm.session, user, org |
x-ci-prompt-cache-session | string | Session id read when the scope is `session`. Send one stable value per conversation or agent run. When absent, a string prompt_cache_key in the request body is used. Without either identifier, the tools, opening system instructions and first conversation message identify the session. Affinity remains isolated to the API key. |
x-ci-model-lock | string | Model Lock. Serve exactly the model named in the body: no model-routing technique, no experiment variant and no admission canary may substitute it. Combined with `X-CI-Experiment` (or a forced experiment variant) it is refused with 400 `model_lock_conflict` before anything is read or reserved. If any stage would still change the model, the request is refused with 500 `model_lock_violation` before reaching a model and nothing is billed. Model routing is held off even when the key, the workspace or `x-ci-route` turns it on. `1` or `true` (any case) turns it on; absent or empty leaves it off. Any other value, including `0` and `false`, is refused with 400 `invalid_contract_header` naming the header in `param`, so a typo never silently means off. Anthropic's error envelope carries no code, so on this path the refusals are told apart by status and message only.1, true |
Request body AnthropicMessagesRequest
| Field | Type | Notes |
|---|---|---|
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
modelrequired | string | |
max_tokensrequired | integer | Required on this endpoint. |
messagesrequired | object[] | |
system | string | array | |
stream | boolean | |
stop_sequences | string[] | |
temperature | number | |
top_p | number | |
tools | object[] | |
tool_choice | object | |
thinking | object |
Additional properties are forwarded to the serving provider rather than rejected.
Response AnthropicMessagesResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/messages/count_tokensCount input tokens
Estimate the input tokens for a Messages request. Makes no upstream call and moves no money, so `max_tokens` is not required.
Request body CountTokensRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
messagesrequired | object[] | |
system | string | array | |
tools | object[] |
Additional properties are forwarded to the serving provider rather than rejected.
Response CountTokensResponse
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Responses
OpenAI Responses. Codex integrates here.
/v1/responsesstreamsCreate a response
OpenAI Responses, the surface Codex uses. Stateless: `store` must be `false`, and `previous_response_id` and `conversation` are refused. Replay prior output items in `input` instead. Namespace tools may mix function and custom/freeform entries, including inside `input[].additional_tools`; namespace and call metadata are preserved in buffered and streamed output. Gemini 3 requests using `reasoning_effort: none` fail with code `unsupported_reasoning_effort`. To use Pro, send a POST request to /v1/responses with store: false and reasoning.mode: "pro". Set reasoning.effort separately to control how much reasoning the model does. Pro requires the Responses endpoint. Chat Completions returns HTTP 400 for Pro requests. The models gpt-5.6-luna, gpt-5.6-terra, gpt-5.6-sol and gpt-6-astra support Pro mode. Set stream: true to receive output as it arrives, or stream: false to receive one complete response. These models use standard mode when reasoning.mode is omitted. For gpt-5.5-pro, the model ID already selects Pro. Omit reasoning.mode and set stream: false. This model does not support streaming. Your price, provider, region and data-retention settings still apply. The provider must also support the features in your request. If no compatible Pro provider is available, the API returns HTTP 503 without charging you. A provider response that does not confirm Pro is rejected without a charge. Other OpenAI Pro model IDs, model IDs with a date suffix, and Pro reasoning replay are not yet supported. This includes requesting reasoning.encrypted_content. Pro can take longer and use more tokens than standard mode. Billing includes the total input and output tokens used across its model work, so input_tokens can exceed your original prompt's token count. The gpt-5.5-pro model has its own pricing. For streaming requests, read the final event as well as the HTTP status. A stream can start with HTTP 200 and later report an error. Pro streams are charged only when a valid final response confirms Pro and includes token usage.
Query parameters
| Field | Type | Notes |
|---|---|---|
x-ci-techniques | string | Set to off to disable optional workspace techniques for a controlled benchmark. Preserves workspace defaults and billing policy; takes precedence over technique opt-in headers.off |
x-ci-prompt-cache | string | Prompt-cache knob. `passthrough` (default, or the API key's `prompt_cache_mode`) forwards the request unchanged; `on` additionally adds provider cache breakpoints for supported models. For Claude, unmarked requests cache the shared system/tools prefix and growing conversation; caller-supplied cache controls and lifetimes are preserved. `off` removes explicit cache controls, disables sticky affinity for this request and, for models that read a cache marker, prefers routes that forward the body as sent over the marketplace; it does not disable a provider's implicit caching.on, off, passthrough |
x-ci-prompt-cache-scope | string | Sticky-affinity scope: `session` (default), `user`, or `org`. Scopes the affinity key that keeps a repeated prompt prefix on the same upstream provider so its cache stays warm.session, user, org |
x-ci-prompt-cache-session | string | Session id read when the scope is `session`. Send one stable value per conversation or agent run. When absent, a string prompt_cache_key in the request body is used. Without either identifier, the tools, opening system instructions and first conversation message identify the session. Affinity remains isolated to the API key. |
x-ci-model-lock | string | Model Lock. Serve exactly the model named in the body: no model-routing technique, no experiment variant and no admission canary may substitute it. Combined with `X-CI-Experiment` (or a forced experiment variant) it is refused with 400 `model_lock_conflict` before anything is read or reserved. If any stage would still change the model, the request is refused with 500 `model_lock_violation` before reaching a model and nothing is billed. Model routing is held off even when the key, the workspace or `x-ci-route` turns it on. `1` or `true` (any case) turns it on; absent or empty leaves it off. Any other value, including `0` and `false`, is refused with 400 `invalid_contract_header` naming the header in `param`, so a typo never silently means off.1, true |
Request body ResponsesRequest
| Field | Type | Notes |
|---|---|---|
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
modelrequired | string | |
storerequired | boolean | Must be false. This endpoint is stateless.false |
inputrequired | any | |
instructions | string | null | |
stream | boolean | |
max_output_tokens | integer | null | |
tools | ResponsesTool[] | |
tool_choice | any | |
parallel_tool_calls | boolean | |
reasoning_effort | string | Alias for reasoning.effort. Supplying conflicting values is a 400. Gemini 3 does not support `none` and returns `unsupported_reasoning_effort`.none, minimal, low, medium, high |
reasoning | object | null | Set mode and effort separately. For gpt-5.5-pro, the model ID selects Pro, so omit mode. |
text | object |
Additional properties are forwarded to the serving provider rather than rejected.
Response ResponsesResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`) Under the client contract also `code: invalid_contract_header` (a bad `x-ci-model-lock` or `x-ci-stream-receipt` value) and `code: model_lock_conflict` (Model Lock combined with an experiment). While the key has an open task budget (`/v1/keys/self/task-budgets`), also `code: task_budget_exhausted` (`param: task_budget`): the request's worst-case charge does not fit what the budget has left, so it was refused before anything was held and nothing was charged. Only with a contract header, when the workspace policy requires a task budget and the key has none open, also `code: task_budget_required` (`param: task_budget`); nothing was held or charged. Only with a contract header, every 402, 403 and 429 refusal on the inference path is answered as this 400 instead, with one of these codes: `code: workspace_monthly_cap_hit` (`param: workspace_monthly_cap`), `code: member_monthly_cap_hit` (`param: member_monthly_cap`), `code: wallet_too_low` (`param: wallet`), `code: wallet_top_up_failed` (`param: wallet`), `code: key_monthly_cap_hit` (`param: key_monthly_cap`), `code: contract_funding_refused`, `code: workspace_membership_ended` (`param: workspace_membership`), `code: key_scope_missing` (`param`: the missing scope), `code: key_network_refused` (`param: key_network`), `code: key_allowlist_refused` (`param: key_allowlist`), `code: key_policy_refused` (`param: key_policy`), `code: provider_policy_block` (`param: end_user`), `code: contract_access_refused`, `code: key_minute_cap_hit` (`param: key_minute_cap`), `code: key_daily_cap_hit` (`param: key_daily_cap`), `code: key_parallel_cap_hit` (`param: key_parallel_cap`), `code: contract_pacing_refused`. Each message starts with a distinct phrase and nothing was charged unless it says otherwise. Without a contract header these refusals keep their usual status and code.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`) Under Model Lock also `code: model_lock_violation`: a stage would have served another model, so the request was refused before reaching any model and nothing was billed.502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Decisions
OpenAI Decisions. Typed questions answered with probabilities by decision models such as Jev.
/v1/decisionsAsk a decision model typed questions
Ask predicate, choice and score questions about one input and receive probabilities instead of text. OpenAI Decisions API shape; the OpenAI SDKs work with only a base URL change. Billed on input tokens only. Streaming is not supported.
Request body DecisionRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | A decision model, such as jev-1.13. |
inputrequired | any | The text every question is asked against: a string, or an array of user messages whose content is a string or a list of input_text parts. |
questionsrequired | array | Each question needs a type and instructions. A choice question lists 2 to 255 choices; a score question lists 2 to 10 levels. Names are optional and must be unique. |
safety_identifier | string | null | Your own id for the end user behind this request. Folded into the upstream safety identity, so a policy block on one end user does not affect your account; the raw value is never forwarded. |
stream | boolean | Decisions are never streamed; only false or absent is accepted.false |
zdr | boolean | Require a zero data retention route. |
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
Additional properties are forwarded to the serving provider rather than rejected.
Response DecisionResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Models
The catalog, its capabilities and its prices.
/v1/modelsList models
Every model this platform serves, with capabilities and prices. `pricing_version` is a SHA-256 over the list that was returned, so a filtered response validates its own cache and not the unfiltered one.
Query parameters
| Field | Type | Notes |
|---|---|---|
type | string | `text` for chat models, `image` for image generation, `video` for video generation, `decision` for decision models served on /v1/decisions.text, image, video, decision |
vision | boolean | Select models that do or do not accept image input. Accepts 1/true/t/yes/y/on and 0/false/f/no/n/off. |
video | boolean | Select models that do or do not accept video input. Accepts 1/true/t/yes/y/on and 0/false/f/no/n/off. |
reasoning | boolean | Select models that do or do not reason. Accepts 1/true/t/yes/y/on and 0/false/f/no/n/off. |
streaming | boolean | Select models that do or do not stream. Accepts 1/true/t/yes/y/on and 0/false/f/no/n/off. |
zdr | boolean | `true` switches to the zero-data-retention view: only models a `zdr: true` request can be served for, priced at what that request pays. `false` (the default) is the standard view, not a filter for models without ZDR supply. |
provider | string | Exact provider name, compared trimmed and case-insensitively. A prefix does not match.length 1–80 |
Response ModelList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/public/modelsList models (public)
The advertised catalog, with live prices, and NO API KEY. This is what the marketing pages and any other surface that cannot hold a key read. Prices are resolved by the same rule that bills a request, so a quote here and the charge cannot disagree. `/v1/models` covers the same catalog for authenticated clients and adds marketplace-only models.
Response PublicModelCatalog
The catalog.
Error statuses (1)
503The catalog could not be read. No prices are served rather than stale ones.
/public/models/{model_id}Get one model (public)
One catalog entry. The id may contain slashes (`google/gemini-3.5-flash-lite`) and is matched exactly, including case.
Query parameters
| Field | Type | Notes |
|---|---|---|
model_idrequired | string | Model id. May contain `/`. |
Response PublicModel
The model.
Error statuses (2)
404No such model, or it is not currently advertised.503The catalog could not be read. No prices are served rather than stale ones.
/v1/pricing/changesPrice changes
Price moves at or after `since`, oldest first, so a client can detect changes without re-diffing the catalogue. Store the `changed_at` of the last change you processed and pass it as `since` on the next poll, then follow `next_cursor` to the end. Requires the `usage:read` scope.
Query parameters
| Field | Type | Notes |
|---|---|---|
sincerequired | string (date-time) | Return changes at or after this time. Required: there is no default window, and an unbounded feed would return the whole price history. |
limit | integer | Maximum changes to return.1–100 |
cursor | string | Opaque cursor from the previous page's `next_cursor`. |
Response PricingChangeList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/models/supplyCount supply under a price ceiling
How many supply sources would currently serve a chat request carrying `min_discount_percent` and/or `zdr=true` for this model, using the same filter the request path applies. At least one of the two is required. Candidates are priced under the ceiling now; health and fill are not guaranteed.
Query parameters
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
min_discount_percent | number | 0–99.99 |
zdr | boolean | Count only zero-data-retention suppliers. |
Response ModelSupply
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/models/comparePrice a workload across models
What a given number of prompt and completion tokens would cost on each of up to ten models, cheapest first. Needs usage:read only. It discloses catalog pricing, nothing about the account.
Query parameters
| Field | Type | Notes |
|---|---|---|
modelsrequired | string | Comma-separated model ids. At most 10. |
prompt_tokens | integer | 0–10000000 |
completion_tokens | integer | 0–10000000 |
requests | integer | Multiply the per-request cost by this. Defaults to 1.min 1 |
Response ModelComparison
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Usage
What the workspace spent. Requires the usage:read scope.
/v1/usage/requestsList requests
Per-request history for the calling workspace, newest first. Requires the `usage:read` scope. `cursor` is opaque and comes from the previous page's `next_cursor`; do not parse it. With `request_ids`, the named requests are returned instead of a page: `has_more` is false, `next_cursor` is null and `missing_request_ids` lists the ids that did not come back.
Query parameters
| Field | Type | Notes |
|---|---|---|
limit | integer | 1–100 |
cursor | string | Opaque cursor from the previous page. |
request_ids | string (uuid)[] | Comma-separated request ids (1 to 100 UUIDs) to look up directly, for a client reconciling its own receipts. Deduplicated and compared case-insensitively. A malformed list or more than 100 entries is a 422 naming the parameter; combining it with `cursor` is a 400. The other filters still apply. Ids that do not exist, ids that are not the caller's, and ids whose detail has passed the 45-day per-request retention window are all reported in `missing_request_ids`, deliberately indistinguishable. A client reconciling receipts older than that window should expect them here rather than treat their absence as a request we never served. |
start_at | string (date-time) | Inclusive lower bound. A value with no offset is read as UTC. |
end_at | string (date-time) | Exclusive upper bound. |
api_key_id | string (uuid) | Limit to one key in the calling workspace. A key outside it is a 404, not an empty list. |
Response UsageRequestList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/usage/dailyDaily spend
Daily spend for the calling workspace, defaulting to the last 30 days and zero-filled across the range so a chart does not close gaps. Requires the `usage:read` scope. Only settled requests contribute to spend.
Query parameters
| Field | Type | Notes |
|---|---|---|
start_at | string (date-time) | Inclusive lower bound. A value with no offset is read as UTC. |
end_at | string (date-time) | Exclusive upper bound. |
api_key_id | string (uuid) | Limit to one key in the calling workspace. A key outside it is a 404, not an empty list. |
timezone_offset_minutes | integer | Minutes east of UTC, deciding which calendar day a request falls on.-840–840 |
Response UsageDaily
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/usage/team-savingsTeam Savings
What the calling key's workspace spent, against the list price of the same model (`baseline.id: same_model_list_price_v1`), in total and by member, project or model, over inclusive UTC days (at most 92). Requires `usage:read`; `scope=team` also requires `account:read`. Includes every key in the workspace, not only Keak Code; requests without a Keak Code project tag are Unattributed. Spend: a settled request at its charge, a failed request that was charged at its charge, a cache hit at $0; in-flight requests are counted as pending with no amount. Requests with no known list price are left out of the baseline and of the comparable spend, and `coverage` says how much. Savings may be negative. Amounts are 6-decimal strings rounded half-even one by one, so a figure derived from two others can differ by $0.000001; groups sum to the totals exactly before rounding. At most 500 groups are returned (`groups_truncated`); totals are always complete. A member is the owner of the key that sent the request.
Query parameters
| Field | Type | Notes |
|---|---|---|
scope | string | `self` (default): the caller's own requests in the key's workspace. `team`: the workspace's members the caller may see, which needs the `account:read` scope as well as a workspace role that includes team reports (owner, admin, or a member granted Team reports). The workspace is always the calling key's; it cannot be named.self, team |
from | string (date) | First UTC day, inclusive. Defaults to 29 days before `to`. The period may cover at most 92 days (`code: range_too_large`). |
to | string (date) | Last UTC day, inclusive. Defaults to today (UTC). |
group_by | string | How `groups` is split. Ignored by the requests listing.member, project, model |
member | string[] | Comma-separated: `me` or up to 50 member user ids. Anyone other than the caller needs `scope=team` (`code: member_requires_team_scope`). |
project | string[] | Comma-separated: up to 50 opaque project ids from Keak Code's attribution, or `unattributed` for requests with no project tag. |
model | string[] | Comma-separated: up to 20 model ids, matched exactly. |
Response TeamSavings
Successful response
Error statuses (15)
400`param` names the parameter. `code: invalid_request` for a malformed, repeated or unknown parameter; `code: range_too_large` (`param: from`) for more than 92 days; `code: member_requires_team_scope` (`param: member`) for another member with `scope=self`; `code: invalid_cursor` for a cursor this service did not issue.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `usage:read`, or `account:read` for `scope=team` (`param` is the scope). `code: team_reports_forbidden` (`param: scope`) when the caller's role does not include team reports, or the key is a personal key. `code: member_not_visible` (`param: member`) for a member not shared with the caller. `code: organization_membership_required` when the key's workspace membership was removed.404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: team_savings_unavailable` before the deployment's database has the report, or `code: report_timeout` when the report took too long; try a shorter period.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/usage/team-savings/requestsTeam Savings request records
The request records behind a Team Savings figure, newest first, with the same scope, filters and permissions as `GET /v1/usage/team-savings`. Records only: no prompt, response or request body. The charged pages of a period sum to the report's `actual_usd`. `cursor` is opaque and comes from the previous page's `next_cursor`.
Query parameters
| Field | Type | Notes |
|---|---|---|
scope | string | `self` (default): the caller's own requests in the key's workspace. `team`: the workspace's members the caller may see, which needs the `account:read` scope as well as a workspace role that includes team reports (owner, admin, or a member granted Team reports). The workspace is always the calling key's; it cannot be named.self, team |
from | string (date) | First UTC day, inclusive. Defaults to 29 days before `to`. The period may cover at most 92 days (`code: range_too_large`). |
to | string (date) | Last UTC day, inclusive. Defaults to today (UTC). |
group_by | string | How `groups` is split. Ignored by the requests listing.member, project, model |
member | string[] | Comma-separated: `me` or up to 50 member user ids. Anyone other than the caller needs `scope=team` (`code: member_requires_team_scope`). |
project | string[] | Comma-separated: up to 50 opaque project ids from Keak Code's attribution, or `unattributed` for requests with no project tag. |
model | string[] | Comma-separated: up to 20 model ids, matched exactly. |
bucket | string | `charged` (default): settled, cached and charged failures, the requests the report's spend is made of. `pending`: in flight. `failed_unbilled`: failed with no charge. `all`: every one.charged, pending, failed_unbilled, all |
limit | integer | 1–100 |
cursor | string | Opaque cursor from the previous page. |
Response TeamSavingsRequestList
Successful response
Error statuses (15)
400`param` names the parameter. `code: invalid_request` for a malformed, repeated or unknown parameter; `code: range_too_large` (`param: from`) for more than 92 days; `code: member_requires_team_scope` (`param: member`) for another member with `scope=self`; `code: invalid_cursor` for a cursor this service did not issue.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `usage:read`, or `account:read` for `scope=team` (`param` is the scope). `code: team_reports_forbidden` (`param: scope`) when the caller's role does not include team reports, or the key is a personal key. `code: member_not_visible` (`param: member`) for a member not shared with the caller. `code: organization_membership_required` when the key's workspace membership was removed.404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: team_savings_unavailable` before the deployment's database has the report, or `code: report_timeout` when the report took too long; try a shorter period.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Feedback
Score a completed request.
/v1/feedbackScore a request
Rate a completed request 1-5. Not present on the legacy service.
Request body FeedbackRequest
| Field | Type | Notes |
|---|---|---|
request_idrequired | string (uuid) | |
scorerequired | integer | 1–5 |
comment | string | null |
Response FeedbackResponse
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Account
Wallet, savings, experiments, techniques, keys and connected chat channels. Reads need the account:read scope and changes need account:write; neither is on a key by default. This is the surface the MCP server drives.
/v1/accountGet the account
Email, workspace and role, how many keys are live and when one was last used, and how many optimization techniques are actually on.
Response Account
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/balanceGet the wallet balance
Balance, what is reserved against requests in flight, and auto-recharge state. Spend against `available_usd`, not `balance_usd`.
Response AccountBalance
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/usageSummarize usage over a window
Requests, billed, saved versus list price and the top models. The rolled-up counterpart to /v1/usage/daily's per-day series.
Query parameters
| Field | Type | Notes |
|---|---|---|
days | integer | Window in days. Defaults to 7, maximum 90.1–90 |
Response AccountUsage
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/savingsGet the savings snapshot
Savings over 24 hours, 7 days and 30 days, every experiment's per-request economics, and the technique catalog with this account's state on each.
Response AccountSavings
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/workspace-policyRead the workspace admin policy
The admin policy of the calling key's workspace, and `can_edit` (owner or admin). Five settings bind every member, owners and admins included: a maximum task limit that clamps every task budget, an advisory default task limit, whether Keak Code contract requests need an open task budget, techniques locked off, and a prompt-cache rule. Requires `account:read`; any member may read it. Free: runs no inference.
Response WorkspaceAdminPolicyView
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `account:read` (`param` is the scope); a temporary Prompt Studio key carries only `inference`.404`code: workspace_not_found`: the key is a personal key, which no workspace policy covers.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: workspace_policy_unavailable`: the policy could not be read or changed, including on a deployment whose database does not have the workspace admin policy migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/workspace-policyReplace the workspace admin policy
Replace the whole policy of the calling key's workspace; the workspace is always the key's own. Send `expected_version` as read: a policy saved in between is 409 with the current one. Lowering `task_limit_max_usd` lowers every open task budget above it in the same transaction (`clamped_task_budget_count`); raising or clearing it changes no budget. Every save is audited. Requires `account:write` and a workspace owner or admin.
Request body WorkspaceAdminPolicyInput
| Field | Type | Notes |
|---|---|---|
expected_versionrequired | integer | The `version` that was read. A policy saved since then answers 409.min 0 |
task_limit_default_usdrequired | string | null | USD as a string, above 0 and at most 10000, at most 6 decimal places; not above the maximum. |
task_limit_max_usdrequired | string | null | USD as a string, above 0 and at most 10000, at most 6 decimal places. |
task_budget_requiredrequired | boolean | |
techniques_locked_offrequired | string[] | Distinct technique ids from the catalog. |
prompt_cache_rulerequired | string | null | on, off, passthrough, null |
Response WorkspaceAdminPolicySaved
Successful response
Error statuses (15)
400`code: invalid_request`, with `param` naming the field: not a JSON object, an unknown or missing field, `expected_version` not a whole number, a money field not null or a decimal string above 0 and at most 10000 with at most 6 places, the default above the maximum, `task_budget_required` not a boolean, `techniques_locked_off` not a list of at most 32 distinct catalog technique ids, or `prompt_cache_rule` not null, on, off or passthrough.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `account:write` (`param` is the scope); a temporary Prompt Studio key carries only `inference`. `code: workspace_manager_required` unless the key's creator is a workspace owner or admin.404`code: workspace_not_found`: the key is a personal key, which no workspace policy covers.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: workspace_admin_policy_conflict`: the policy was saved since `expected_version` was read. The current policy is in `error.current`; nothing was changed.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: workspace_policy_unavailable`: the policy could not be read or changed, including on a deployment whose database does not have the workspace admin policy migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/task-budgetsList the workspace's task budgets
Every member's task budgets in the calling key's workspace, newest first, each with its member and key. Requires `account:read` and a workspace owner or admin.
Query parameters
| Field | Type | Notes |
|---|---|---|
status | string | `open` (open and unexpired, the default) or `recent` (opened in the last 7 days, any status).open, recent |
limit | integer | How many budgets, 1 to 200. Defaults to 100.1–200 |
Response AdminTaskBudgetList
Successful response
Error statuses (15)
400`code: invalid_request` (`param: status` or `limit`) for a status other than open or recent, or a limit outside 1 to 200.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `account:read` (`param` is the scope); a temporary Prompt Studio key carries only `inference`. `code: workspace_manager_required` unless the key's creator is a workspace owner or admin.404`code: workspace_not_found`: the key is a personal key, which no workspace policy covers.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: workspace_policy_unavailable`: the policy could not be read or changed, including on a deployment whose database does not have the workspace admin policy migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/task-budgets/{budget_id}Read one of the workspace's task budgets
One budget in the calling key's workspace. With `include=requests`, up to 500 of the requests it held, oldest first, and `requests_truncated`; that also needs the caller to see the member's requests in Team reports. Requires `account:read` and a workspace owner or admin.
Query parameters
| Field | Type | Notes |
|---|---|---|
budget_idrequired | string (uuid) | A budget in the calling key's workspace. Another workspace's budget id answers 404. |
include | string | `requests` adds the requests this budget held.requests |
Response AdminTaskBudget
Successful response
Error statuses (15)
400`code: invalid_request` (`param: include`) for an `include` other than `requests`.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `account:read` (`param` is the scope); a temporary Prompt Studio key carries only `inference`. `code: workspace_manager_required` unless the key's creator is a workspace owner or admin. With `include=requests`, `code: team_reports_forbidden` or `member_not_visible` when the caller may not see that member's requests.404`code: workspace_not_found`: the key is a personal key, which no workspace policy covers. `code: task_budget_not_found` (`param: budget_id`): no such budget in this workspace.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: workspace_policy_unavailable`: the policy could not be read or changed, including on a deployment whose database does not have the workspace admin policy migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/account/task-budgets/{budget_id}/closeClose a member's task budget
Close a budget in the calling key's workspace now: `close_reason: admin` and `closed_by_user_id`, audited. Requests it already held settle into it later. Idempotent: a closed or expired budget is answered as it is. The member may open a new budget. Body `{}` or none. Requires `account:write` and a workspace owner or admin.
Query parameters
| Field | Type | Notes |
|---|---|---|
budget_idrequired | string (uuid) | A budget in the calling key's workspace. Another workspace's budget id answers 404. |
Response AdminTaskBudget
Successful response
Error statuses (15)
400`code: invalid_request` for a body that is not empty, `{}`, or a JSON object without fields.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `account:write` (`param` is the scope); a temporary Prompt Studio key carries only `inference`. `code: workspace_manager_required` unless the key's creator is a workspace owner or admin.404`code: workspace_not_found`: the key is a personal key, which no workspace policy covers. `code: task_budget_not_found` (`param: budget_id`): no such budget in this workspace.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: workspace_policy_unavailable`: the policy could not be read or changed, including on a deployment whose database does not have the workspace admin policy migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/experimentsList experiments
Every experiment on the account with its cost, savings and feedback.
Response ExperimentList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/experiments/{experiment_id}Get an experiment
One experiment broken down per variant, so arms can be compared.
Query parameters
| Field | Type | Notes |
|---|---|---|
experiment_idrequired | string (uuid) |
Response Experiment
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/techniquesList optimization techniques
The catalog with this account's state on each entry. Preview and unimplemented techniques are included and flagged, not hidden.
Response TechniqueList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/techniques/{technique_id}Enable or disable a technique
Requires account:write. A technique that is not implemented, or is deprecated, is refused with a 404. Turning on a technique the workspace admin policy locks off is refused with 409 technique_locked_by_workspace; turning it off is always allowed.
Query parameters
| Field | Type | Notes |
|---|---|---|
technique_idrequired | string |
Request body TechniqueUpdateRequest
| Field | Type | Notes |
|---|---|---|
enabledrequired | boolean |
Response TechniqueUpdateResponse
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keysList API keys
Prefixes only. Saved full keys can be copied by their owner in the dashboard.
Query parameters
| Field | Type | Notes |
|---|---|---|
include_revoked | boolean | Include revoked keys. Defaults to false. Accepts 1/true/t/yes/y/on and 0/false/f/no/n/off. |
Response ApiKeyList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keysCreate an API key
Requires account:write, and refuses a caller authenticated through a chat integration: a short-lived internal token must not be able to mint a permanent credential. The plaintext key is returned in the response. Its owner can also copy the saved key in the dashboard.
Request body ApiKeyCreateRequest
| Field | Type | Notes |
|---|---|---|
name | string | length 0–64 |
organization_id | string | null | |
scopes | string[] | Defaults to inference and usage:read. |
allowed_models | string[] | |
allowed_ip_cidrs | string[] | |
expires_at | string | null | |
rate_limit_per_minute | integer | null | |
daily_request_limit | integer | null | |
concurrency_limit | integer | null | |
monthly_spend_limit_usd | number | null |
Response ApiKeyCreated
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}Revoke an API key
Requires account:write. Revoking the key you are authenticating with is allowed. The next request simply fails to authenticate.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string (uuid) |
Response ApiKeyRevoked
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/channelsList connected chat channels
Every Slack, Discord, Telegram and email channel this account has connected, plus the event catalog PATCH accepts.
Response ChannelList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/channels/{channel_id}Get a channel
One channel's subscriptions and digest schedule.
Query parameters
| Field | Type | Notes |
|---|---|---|
channel_idrequired | string (uuid) |
Response Channel
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/channels/{channel_id}Change a channel's schedule or subscriptions
Requires account:write. An events list containing an unknown id is refused whole rather than partially applied.
Query parameters
| Field | Type | Notes |
|---|---|---|
channel_idrequired | string (uuid) |
Request body ChannelUpdateRequest
| Field | Type | Notes |
|---|---|---|
report_frequency | string | daily, weekly, monthly, off |
events | string[] |
Response Channel
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/channels/{channel_id}/send-reportPost the savings digest now
Requires account:write. Throttled to one send per channel per minute, and does not consume the next scheduled digest.
Query parameters
| Field | Type | Notes |
|---|---|---|
channel_idrequired | string (uuid) |
Response ChannelReportResponse
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/security/strikesRecord a refused request against the calling account
Reported by the MCP server when it refuses a call. `injection`, `escalation` and `policy` describe hostile arguments and trigger a permanent ban on the first occurrence, across every surface, not only the account API. `unauthorized` and `forbidden` describe a credential rather than an intent (a rotated key, a missing scope), so they are accepted and have no consequence. There is no rolling window, no warning tier and no escalation ladder. No scope is required. Returns the account's standing, and files a support ticket on the account's behalf so a block has somewhere to be contested.
Request body SecurityStrikeRequest
| Field | Type | Notes |
|---|---|---|
toolrequired | string | The MCP tool that was called.length 0–80 |
kindrequired | string | What was refused. `injection`, `escalation` and `policy` are refusals the MCP server issued itself and each count as a strike; `unauthorized` (401) and `forbidden` (403) are API denials and count once per distinct `detail`.injection, escalation, policy, unauthorized, forbidden |
detailrequired | string | A short stable code for the reason. Never the offending text.length 0–200 |
Response SecurityStanding
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/security/standingGet the calling account's standing
Whether the account is blocked, and where to appeal. A block is a ban: it has no expiry, so `blocked_until` is always null and `permanent` is true whenever `tier` is `blocked`. The counting fields (`strikes`, `attempts`, `remaining`, `warn_after`, `block_after`, `window_hours`) are retained for compatibility with deployed MCP clients and now describe a one-strike policy rather than a tally. No scope is required. A banned account is refused at authentication, so this route answers for accounts that are not blocked, which lets a client notice a lift.
Response SecurityStanding
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/billing/topupStart a wallet top-up
Requires account:write. Creates a Stripe Checkout session and returns its link. Nothing is charged here. The customer completes the payment on Stripe's own page, with the amount in front of them.
Request body TopupRequest
| Field | Type | Notes |
|---|---|---|
amount_usdrequired | number | 5–1000 |
Response TopupResponse
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Docs
The product documentation, searchable. Public content, so any valid key can read it without an additional scope. It exists as an endpoint so an assistant can answer 'how do I…' from the shipped text rather than from memory.
/v1/docsSearch the documentation
With `q`, the matching sections in full, best match first. Without it, the table of contents includes every section's id, title, category and link without bodies. A client can see what exists before choosing what to read. Needs a valid key and no scope: this is the same text served at /docs.
Query parameters
| Field | Type | Notes |
|---|---|---|
q | string | What to look up. Keywords beat sentences: the corpus is a couple of dozen sections and matching is on titles, keywords and body text. |
limit | integer | Maximum sections to return. Ignored without `q`.1–10 |
Response DocSectionList
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Keak Code
The opt-in client contract the Keak Code VS Code extension uses: the calling key's technique profile, task budgets and request attribution, alongside the `x-ci-model-lock` and `x-ci-stream-receipt` headers on the inference endpoints. These routes need the `inference` scope, not `account:read`.
/v1/usage/attributionsAttribute your requests to a task
Tag requests made with the calling key with a client task, an opaque project id and the client name. Requires the `inference` scope (describing your own traffic is part of sending it); only an API key authenticates here. All or nothing: every listed request must belong to the calling key and its actor, otherwise nothing is written. A tag never changes once written: a different task, project or client for an already tagged request is a 409, and repeating an identical post is a no-op that counts the ids as `unchanged`. The tag is read back as `attribution` on `GET /v1/usage/requests`.
Request body UsageAttributionRequest
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | The client's task id. |
project_id | string | null | An opaque client-side hash identifying the project, never a readable path or URL. Optional; null when the client has none. |
clientrequired | string | keak-code |
request_idsrequired | string (uuid)[] | Request ids from the receipts. Deduplicated and lower-cased. |
Response UsageAttribution
Successful response
Error statuses (15)
400Malformed body (`code: invalid_request`), with `param` naming the field: not JSON, an unknown field, `task_id` not a UUID, `project_id` not null or 16 to 64 characters of A-Z, a-z, 0-9, `_` or `-`, `client` other than `keak-code`, or `request_ids` not 1 to 100 UUIDs.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope).404`code: request_not_found`: some listed requests were not made with the calling key, do not exist, or are older than the 45-day per-request retention window; the three are indistinguishable. They are listed in `error.missing_request_ids`, inside the `error` object. Nothing was written.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: attribution_conflict`: some listed requests already carry a different task, project or client, listed in `error.conflicting_request_ids`. Nothing was written for them; a tag never changes once written.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: attribution_unavailable`: the attribution could not be read or recorded, including before the request_tags migration is applied. Safe to retry: the post is idempotent.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/usage/task-outcomesReport a completed task
Record that a task the calling key already attributed (`POST /v1/usage/attributions`) was completed. Requires the `inference` scope; only an API key authenticates here. `completed` is the only outcome recorded: a Keak Code task can be resumed after it failed or was cancelled, so those are not final. Idempotent: repeating it answers 200 with `created: false`. The workspace is copied from the task's tags and the member from the key. Team Savings reads it as "completed tasks, as reported by Keak Code".
Request body UsageTaskOutcomeRequest
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | The task id used in the attribution. |
outcomerequired | string | completed |
clientrequired | string | keak-code |
Response UsageTaskOutcome
Successful response
Error statuses (15)
400Malformed body (`code: invalid_request`), with `param` naming the field: not JSON, an unknown field, `task_id` not a UUID, `outcome` other than `completed`, or `client` other than `keak-code`.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope).404`code: task_not_found` (`param: task_id`): no request made with the calling key is tagged with this task. Attribute the requests first (`POST /v1/usage/attributions`).408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: task_spans_workspaces` (`param: task_id`): the task's requests are tagged in more than one workspace, so the outcome is not recorded.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: task_outcomes_unavailable`: the outcome could not be read or recorded, including before the task_outcomes migration is applied. Safe to retry: the post is idempotent.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}/techniquesRead the calling key's technique profile
What each Cheaper Inference technique will do for requests made with this key, derived from the same resolver that serves requests: the key's own override, the workspace setting, the effective state and where it comes from. Also the key's id, prefix, scopes, monthly spend limit and `prompt_cache_mode`. On a workspace key also the workspace admin policy (`admin_policy`): a technique it locks off reads `locked_by_workspace: true` and `effective: false`, and `prompt_cache_mode_effective` applies its cache rule. Requires the `inference` scope. Only `self` is accepted as `key_id` in this release.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string | `self`: the API key making the request. Any other value is a 404 in this release.self |
Response KeyTechniqueProfile
Successful response
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope). `code: api_key_required` for a caller with no API key behind it.404`code: api_key_not_found`: `key_id` is not `self`, or the key was revoked after it authenticated.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: delegated_api_key`: a temporary Prompt Studio key authenticates as its parent, so `self` would describe another key. Use the selected API key instead.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: technique_settings_unavailable`: the profile could not be read. Retry shortly.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}/techniquesNarrow the calling key's technique profile
Move the key's own techniques toward conservative and set its prompt-cache default. `profile: "coding-safe-v1"` writes an explicit off override for every implemented technique; entries in `techniques` then apply on top of it. `off` is always allowed; `inherit` only while the workspace has that technique off; `on` never (403 `technique_change_requires_manager`, turned on by the key's manager in the dashboard). Unknown fields are refused. Requires the `inference` scope. Only `self` is accepted as `key_id`. Answers with the profile read back after the change. Under a workspace `prompt_cache_rule`, `prompt_cache_mode` is still stored as the key's default and the answer is 200: `prompt_cache_mode` shows it and `prompt_cache_mode_effective` the rule. `inherit` on a technique the workspace locks off is refused like any `inherit` while the workspace has it on, because it would turn on when the lock is lifted.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string | `self`: the API key making the request. Any other value is a 404 in this release.self |
Request body KeyTechniqueProfileUpdate
| Field | Type | Notes |
|---|---|---|
profile | string | An explicit off override for every implemented technique.coding-safe-v1 |
techniques | map<string, string> | Technique id to state, applied on top of `profile`. `on` is always refused with 403, and `inherit` while the workspace has the technique on. |
prompt_cache_mode | string | on, off, passthrough |
Response KeyTechniqueProfile
Successful response
Error statuses (15)
400Malformed body (`code: invalid_request`): not a JSON object, empty, an unknown field, an unknown technique id, a state other than on/off/inherit, or a `prompt_cache_mode` other than on/off/passthrough. `param` names the field, for example `techniques.<id>`.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope). `code: api_key_required` for a caller with no API key behind it. `code: technique_change_requires_manager` (`param: techniques.<id>`) for `on`, or for `inherit` while the workspace has that technique on, because either would turn it on for this key. `code: api_key_not_manageable` when the caller cannot manage the key. Nothing is written on any 403.404`code: api_key_not_found`: `key_id` is not `self`, or the key was revoked after it authenticated.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: delegated_api_key`: a temporary Prompt Studio key authenticates as its parent, so `self` would describe another key. Use the selected API key instead.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: technique_settings_unavailable`: the profile could not be read. Retry shortly.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}/task-budgetsList the calling key's task budgets
The key's open task budget (null when none is open and unexpired) and its 20 newest budgets, newest first. A task budget is a spending cap Cheaper Inference enforces on this key while it is open: every inference request the key admits holds its worst-case charge against the budget before any wallet hold, and one that does not fit is refused with 400 `code: task_budget_exhausted`. Enforcement is per key, not per task: every request on the key counts, whichever client sent it, and only requests that reach Cheaper Inference are held. Also the workspace admin policy (`admin_policy`). Requires the `inference` scope; only `self` is accepted as `key_id`. Free: runs no inference.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string | `self`: the API key making the request. Any other value is a 404 in this release.self |
Response TaskBudgetList
Successful response
Error statuses (15)
400`code: invalid_request` (`param: include`) for an `include` other than `requests`.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope). `code: api_key_required` for a caller with no API key behind it.404`code: api_key_not_found`: `key_id` is not `self`, or the key was revoked or expired.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: delegated_api_key`: a temporary Prompt Studio key authenticates as its parent, so `self` would describe another key.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: task_budget_unavailable`: the budget could not be read or changed, including on a deployment whose database does not have the task budget migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}/task-budgetsOpen a task budget on the calling key
Open the key's task budget for one client task, with a limit and a lease. Until it is closed or the lease expires, each request on the key holds its worst-case charge against the limit and settles to what it was billed; a request that does not fit is refused before anything is held. At expiry enforcement ends and the key falls back to its normal limits, so renew the lease with PATCH while the task runs. One open budget per key: repeating the same task and limit replays it (200, `replayed: true`); the same task with another limit is 409 `task_budget_exists` (raise it with PATCH instead); another task's open budget is 409 `task_budget_conflict` with that budget in `error.active`. On a workspace key, a limit above the workspace admin policy's maximum is clamped to it, never refused: `limit_usd` is the maximum and `clamped_to_max` is true. A replay compares the clamped limit.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string | `self`: the API key making the request. Any other value is a 404 in this release.self |
Request body TaskBudgetOpenRequest
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | The client's task id. |
limit_usdrequired | string | USD, above 0 and at most 10000, at most 6 decimal places. A string, not a number. |
expires_in_srequired | integer | The lease, in seconds from now.60–86400 |
client | string | keak-code |
Response TaskBudget
Already open for this task with this limit (`replayed: true`).
Error statuses (16)
201Opened (`replayed: false`).400Malformed body (`code: invalid_request`), with `param` naming the field: not a JSON object, an unknown field, `task_id` not a UUID, `limit_usd` not a decimal string with at most 6 places above 0 and at most 10000 (an exponent or a JSON number is refused), `expires_in_s` not a whole number from 60 to 86400, or `force` not a boolean.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope). `code: api_key_required` for a caller with no API key behind it.404`code: api_key_not_found`: `key_id` is not `self`, or the key was revoked or expired.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: delegated_api_key`: a temporary Prompt Studio key authenticates as its parent, so `self` would describe another key. `code: task_budget_conflict`: another task's budget is open on this key, in `error.active`. `code: task_budget_exists`: this task's budget is open with another limit, in `error.active`.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: task_budget_unavailable`: the budget could not be read or changed, including on a deployment whose database does not have the task budget migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}/task-budgets/{budget_id}Read one of the calling key's task budgets
The budget as it is now. `status` is the effective one: an open budget past its lease reads `expired`. `read_at` is the server's clock, for lease arithmetic. With `include=requests`, up to 500 of the requests it held, oldest first, and `requests_truncated`.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string | `self`: the API key making the request. Any other value is a 404 in this release.self |
budget_idrequired | string (uuid) | A budget of the calling key. Another key's budget id answers 404, like one that does not exist. |
include | string | `requests` adds the requests this budget held.requests |
Response TaskBudget
Successful response
Error statuses (15)
400`code: invalid_request` (`param: include`) for an `include` other than `requests`.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope). `code: api_key_required` for a caller with no API key behind it.404`code: api_key_not_found`: `key_id` is not `self`, or the key was revoked or expired. `code: task_budget_not_found` (`param: budget_id`): no such budget on this key.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: delegated_api_key`: a temporary Prompt Studio key authenticates as its parent, so `self` would describe another key.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: task_budget_unavailable`: the budget could not be read or changed, including on a deployment whose database does not have the task budget migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}/task-budgets/{budget_id}Raise a task budget or renew its lease
Raise the limit, renew the lease from now, or both. The limit asked for never goes down. Only the budget of the task named in the body changes, and only while it is open. On a workspace key a raise above the workspace admin policy's maximum lands at it (`clamped_to_max: true`), and a renewal alone of a budget above the maximum lowers it to the maximum.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string | `self`: the API key making the request. Any other value is a 404 in this release.self |
budget_idrequired | string (uuid) | A budget of the calling key. Another key's budget id answers 404, like one that does not exist. |
Request body TaskBudgetUpdateRequest
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | Must be the budget's task. |
limit_usd | string | The new limit; not below the current one. |
expires_in_s | integer | Renew the lease to this many seconds from now.60–86400 |
Response TaskBudget
Successful response
Error statuses (15)
400Malformed body (`code: invalid_request`), with `param` naming the field: not a JSON object, an unknown field, `task_id` not a UUID, `limit_usd` not a decimal string with at most 6 places above 0 and at most 10000 (an exponent or a JSON number is refused), `expires_in_s` not a whole number from 60 to 86400, or `force` not a boolean.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope). `code: api_key_required` for a caller with no API key behind it.404`code: api_key_not_found`: `key_id` is not `self`, or the key was revoked or expired. `code: task_budget_not_found` (`param: budget_id`): no such budget on this key.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: delegated_api_key`: a temporary Prompt Studio key authenticates as its parent, so `self` would describe another key. `code: task_budget_not_open`: closed or expired, the budget in `error.budget`. `code: task_budget_limit_decrease`: a lower limit, the budget in `error.budget`. `code: task_budget_task_mismatch`: the budget belongs to another task, in `error.active`.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: task_budget_unavailable`: the budget could not be read or changed, including on a deployment whose database does not have the task budget migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/keys/{key_id}/task-budgets/{budget_id}/closeClose a task budget
Close the budget now; requests on the key are no longer held by it. Requests it already held stay counted and settle into it later. Idempotent: a closed or expired budget is answered as it is. Closing another task's budget needs `force: true`.
Query parameters
| Field | Type | Notes |
|---|---|---|
key_idrequired | string | `self`: the API key making the request. Any other value is a 404 in this release.self |
budget_idrequired | string (uuid) | A budget of the calling key. Another key's budget id answers 404, like one that does not exist. |
Request body TaskBudgetCloseRequest
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | |
force | boolean | Close the budget even when it belongs to another task. Send only on a user's request. |
Response TaskBudget
Successful response
Error statuses (15)
400Malformed body (`code: invalid_request`), with `param` naming the field: not a JSON object, an unknown field, `task_id` not a UUID, `limit_usd` not a decimal string with at most 6 places above 0 and at most 10000 (an exponent or a JSON number is refused), `expires_in_s` not a whole number from 60 to 86400, or `force` not a boolean.401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403`code: insufficient_scope` when the key lacks `inference` (`param` is the scope). `code: api_key_required` for a caller with no API key behind it.404`code: api_key_not_found`: `key_id` is not `self`, or the key was revoked or expired. `code: task_budget_not_found` (`param: budget_id`): no such budget on this key.408Request timed out (`type: invalid_request_error`, `code: request_failed`)409`code: delegated_api_key`: a temporary Prompt Studio key authenticates as its parent, so `self` would describe another key. `code: task_budget_task_mismatch`: another task's budget and no `force`, in `error.active`.413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503`code: task_budget_unavailable`: the budget could not be read or changed, including on a deployment whose database does not have the task budget migration yet. Safe to retry.504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Videos
/v1/videos/generationsGenerate a video
Generate a video and wait for the completed result. Streaming is not supported.
Request body VideoGenerationRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
promptrequired | string | length 1–∞ |
duration_seconds | number | |
duration | number | Alias for duration_seconds. |
resolution | string | |
aspect_ratio | string | |
audio | boolean | |
image_url | string | |
input_references | string[] | |
n | integer | 1–10 |
stream | boolean | false |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
Additional properties are forwarded to the serving provider rather than rejected.
Response VideoResponse
Successful response
x-ci-zdr— Whether zero data retention applied to this request. Absent when no policy was recorded.x-ci-discount-percent— Quoted customer rate discount versus list, not the final effective savings after usage and optimizations. See usage history for the settled effective discount.X-Cheaper-Inference-Request-Id— Request identifier. Legacy name, still emitted; identical to x-ci-request-id.x-ci-request-id— Request identifier. Canonical going forward.x-ci-tokens-saved— Input tokens removed by the optimizer.x-ci-saved-usd— Total saved against the provider's list price, in USD.x-ci-techniques— Comma-separated ids of the optimization techniques applied. Empty when none were.x-ci-cache— `hit` or `miss` for the exact-match cache.x-ci-prompt-cache-affinity— Sticky prompt-cache outcome (KEA-5164). One of `hit`, `moved`, `new`, `stale`. `hit` means the request was served on the route its conversation was warm on; `moved` means a warm route was remembered but another route served this turn, so its cache was not reused. Present only when the request carried a cacheable prefix and the affinity mode was not off; the exact-match `cache` header covers the separate response cache.x-ci-experiment-variant— Name of the assigned experiment variant. Present only when the request included X-CI-Experiment and matched a running experiment.x-ci-routing-overhead-ms— Elapsed milliseconds from proxy request start to dispatch of the successful provider attempt, including routing and prior attempts. On an exact-match cache hit, measures elapsed proxy processing time before returning the cached response. Present on successful buffered, streaming, and cached responses.x-ci-model-response-ms— Elapsed milliseconds from dispatch of the successful provider attempt to its complete response body. Present on successful non-streaming responses; absent on streaming responses and when no provider was called.x-ci-warning— Present when the request was served with a parameter value other than the one sent, because the model does not accept the value sent. Names the parameter, the value sent and the value used. Example: GPT-6 reasoning_effort "minimal" is served as "low". A request pinned to a provider is refused with 400 unsupported_parameter instead.x-ci-stage-timing— Optional diagnostic timing breakdown, emitted when PROXY_STAGE_TIMING=1 and a request timer is active. Comma-separated stage=milliseconds entries with total first; zero-duration stages are omitted. Stage names depend on the request path.
Error statuses (15)
400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Uploads
/v1/uploadsUpload a temporary image
Store an image for a short time and receive a `file_id`. Send that id in a chat message and it is swapped for a signed URL on the way to the provider. At most 4 MB per image; the whole request body may not exceed 4.5 MB. Rejected for an account with zero data retention.
Error statuses (16)
201The upload was stored400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
/v1/uploads/{upload_id}Delete a temporary image
Remove an upload before it expires. An id belonging to another account answers 404, exactly as an unknown one does.
Query parameters
| Field | Type | Notes |
|---|---|---|
upload_idrequired | string |
Error statuses (16)
204The upload was deleted400Malformed request (`type: invalid_request_error`, `code: invalid_request`)401Invalid or missing API key (`type: authentication_error`, `code: invalid_api_key`)402Insufficient wallet balance (`type: invalid_request_error`, `code: insufficient_balance`)403The API key lacks the scope this endpoint requires (`type: permission_denied_error`, `code: permission_denied`)404Not found (`type: invalid_request_error`, `code: not_found`)408Request timed out (`type: invalid_request_error`, `code: request_failed`)409Conflict (`type: invalid_request_error`, `code: conflict`)413Request too large (`type: invalid_request_error`, `code: request_too_large`)422A parameter failed validation (`type: invalid_request_error`, `code: invalid_request`)425Too early (`type: invalid_request_error`, `code: request_failed`)429Rate limited (`type: rate_limit_error`, `code: rate_limit_exceeded`)500Server error (`type: api_error`, `code: server_error`)502The upstream provider returned an unusable response (`type: api_error`, `code: upstream_error`)503Service unavailable (`type: api_error`, `code: service_unavailable`)504Upstream timeout (`type: api_error`, `code: upstream_timeout`)
Schemas
Every object named above, with the fields the API declares. Objects marked as forwarding additional properties accept fields not listed here and pass them to the serving provider.
OpenAIError
Every `/v1/*` path except `/v1/messages*`.
| Field | Type | Notes |
|---|---|---|
errorrequired | object |
AnthropicError
`/v1/messages` and `/v1/messages/count_tokens`. No `code`, no `param`.
| Field | Type | Notes |
|---|---|---|
typerequired | string | error |
errorrequired | object |
ChatCompletionRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
messagesrequired | object[] | |
stream | boolean | |
max_tokens | integer | |
max_completion_tokens | integer | Alternative output-token limit. If both limits are supplied, they must match. |
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
temperature | number | Model-specific sampling control. Omit on models that do not support sampling. Claude Chat-to-native translation caps values above 1 at 1. |
top_p | number | Model-specific sampling control. Omit on models that do not support it. |
n | integer | Number of completions. Claude supports only 1. |
tools | object[] | |
tool_choice | any | |
reasoning_effort | string | Alias for reasoning.effort, including Claude. Conflicting values return 400. Supported levels depend on the model; Gemini 3 accepts minimal, low, medium, and high only. Claude native bridges translate effort into adaptive thinking or a manual budget within max_tokens. A manual budget must be an integer of at least 1024, strictly below the output limit. Thinking summaries, when returned, appear in message.reasoning_content or delta.reasoning_content. Native thinking and output_config settings take precedence.none, minimal, low, medium, high, xhigh, max |
reasoning | object | boolean | null | Portable reasoning controls, including Claude. false disables thinking where supported. Use /v1/responses for Pro. Chat Completions returns HTTP 400 for reasoning.mode: pro or an OpenAI Pro model ID such as gpt-5.5-pro. |
thinking | object | Native Claude thinking configuration. Takes precedence over portable effort. Active thinking supports auto or none tool_choice, not required or a forced named tool. |
output_config | object | Native Claude output configuration, including effort for adaptive thinking. Preserved by the native Chat bridges. |
include_reasoning | boolean | Claude Chat thinking summaries are hidden by default. Set true (or reasoning.exclude: false) to include them. False or reasoning.exclude: true always hides them without disabling thinking or changing token billing. |
cache_control | object | Request-level Claude cache control. Supported controls on tools, messages and content blocks are also preserved by native bridges. An opt-in does not guarantee a cache hit; off or ZDR removes explicit controls. |
Additional properties are forwarded rather than rejected.
ChatCompletionResponse
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
object | string | |
created | integer | |
model | string | |
choicesrequired | object[] | |
usage | object | |
request_id | string (uuid) | The id to quote when contacting support about this request. |
billing | object | What this request was billed. Present on every successful response. |
Additional properties are forwarded rather than rejected.
CompletionRequest
| Field | Type | Notes |
|---|---|---|
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
modelrequired | string | |
promptrequired | string | array | |
stream | boolean | |
max_tokens | integer |
Additional properties are forwarded rather than rejected.
CompletionResponse
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
choicesrequired | object[] | |
usage | object | |
request_id | string (uuid) | The id to quote when contacting support about this request. |
billing | object | What this request was billed. Present on every successful response. |
Additional properties are forwarded rather than rejected.
VideoGenerationRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
promptrequired | string | length 1–∞ |
duration_seconds | number | |
duration | number | Alias for duration_seconds. |
resolution | string | |
aspect_ratio | string | |
audio | boolean | |
image_url | string | |
input_references | string[] | |
n | integer | 1–10 |
stream | boolean | false |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
Additional properties are forwarded rather than rejected.
VideoResponse
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
statusrequired | string | completed, succeeded |
duration_seconds | number | string | |
videosrequired | object[] | |
usage | object | |
request_id | string (uuid) | The id to quote when contacting support about this request. |
billing | object | What this request was billed. Present on every successful response. |
Additional properties are forwarded rather than rejected.
DecisionRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | A decision model, such as jev-1.13. |
inputrequired | any | The text every question is asked against: a string, or an array of user messages whose content is a string or a list of input_text parts. |
questionsrequired | array | Each question needs a type and instructions. A choice question lists 2 to 255 choices; a score question lists 2 to 10 levels. Names are optional and must be unique. |
safety_identifier | string | null | Your own id for the end user behind this request. Folded into the upstream safety identity, so a policy block on one end user does not affect your account; the raw value is never forwarded. |
stream | boolean | Decisions are never streamed; only false or absent is accepted.false |
zdr | boolean | Require a zero data retention route. |
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
Additional properties are forwarded rather than rejected.
DecisionPredicateQuestion
A yes or no question, answered with the probability of yes.
| Field | Type | Notes |
|---|---|---|
typerequired | string | predicate |
name | string | Optional; unique within the request.length 1–256 |
instructionsrequired | string | length 1–∞ |
Additional properties are forwarded rather than rejected.
DecisionChoiceQuestion
A pick from the listed choices, answered with the chosen value and a probability per choice.
| Field | Type | Notes |
|---|---|---|
typerequired | string | choice |
name | string | Optional; unique within the request.length 1–256 |
instructionsrequired | string | length 1–∞ |
choicesrequired | object[] | Distinct values. Values are typed: a string and a boolean with the same text are two choices. |
Additional properties are forwarded rather than rejected.
DecisionScoreQuestion
A rating on the listed levels, answered with a probability-weighted level index and a probability per level.
| Field | Type | Notes |
|---|---|---|
typerequired | string | score |
name | string | Optional; unique within the request.length 1–256 |
instructionsrequired | string | length 1–∞ |
levelsrequired | object[] | Ordered from lowest to highest. |
Additional properties are forwarded rather than rejected.
DecisionResponse
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
modelrequired | string | |
answersrequired | array | One answer per question, in request order. A predicate carries the probability of yes; a choice carries the chosen value, a confidence and a probability per choice; a score carries the probability-weighted level index, a confidence and a probability per level. A question the model declines comes back as type refusal. |
usagerequired | DecisionUsage |
Additional properties are forwarded rather than rejected.
DecisionPredicateAnswer
| Field | Type | Notes |
|---|---|---|
typerequired | string | predicate |
namerequired | string | null | The question's name, null for an unnamed question. |
probabilityrequired | number | The probability that the statement holds.0–1 |
Additional properties are forwarded rather than rejected.
DecisionChoiceAnswer
| Field | Type | Notes |
|---|---|---|
typerequired | string | choice |
namerequired | string | null | The question's name, null for an unnamed question. |
choicerequired | string | boolean | The chosen value, typed as the question offered it. |
confidencerequired | number | 0–1 |
probabilitiesrequired | object[] | One entry per offered choice. |
Additional properties are forwarded rather than rejected.
DecisionScoreAnswer
| Field | Type | Notes |
|---|---|---|
typerequired | string | score |
namerequired | string | null | The question's name, null for an unnamed question. |
scorerequired | number | The probability-weighted level index, from 0 to the number of levels minus one. |
confidencerequired | number | 0–1 |
probabilitiesrequired | object[] | One entry per level when the model reports them; may be empty. |
Additional properties are forwarded rather than rejected.
DecisionRefusalAnswer
The model declined to answer this question. Other questions in the same request can still receive answers.
| Field | Type | Notes |
|---|---|---|
typerequired | string | refusal |
namerequired | string | null | The question's name, null for an unnamed question. |
Additional properties are forwarded rather than rejected.
DecisionUsage
Billed on input tokens; a decision has no cache or reasoning tokens, so the detail counters are zero.
| Field | Type | Notes |
|---|---|---|
input_tokensrequired | integer | |
input_tokens_detailsrequired | object | |
output_tokensrequired | integer | |
output_tokens_detailsrequired | object | |
total_tokensrequired | integer |
Additional properties are forwarded rather than rejected.
ImageGenerationRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
promptrequired | string | length 1–∞ |
n | integer | 1–10 |
size | string | |
quality | string | |
response_format | string |
Additional properties are forwarded rather than rejected.
ImageEditJsonRequest
CheaperInference JSON extension. URL references are never fetched or stored by CheaperInference and are eligible only on provider routes that accept URLs.
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
promptrequired | string | length 1–32000 |
input_referencesrequired | string[] | |
mask_reference | string | Optional PNG data URL. External mask URLs are not accepted. |
n | integer | 1–10 |
size | string | |
quality | string | |
background | string | |
input_fidelity | string | |
output_format | string | |
output_compression | integer | 0–100 |
response_format | string | |
user | string | |
stream | boolean | false |
ImageEditMultipartRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
promptrequired | string | length 1–32000 |
imagerequired | any | One to 10 PNG, JPEG, or WebP uploads. Decoded sources plus the optional mask must total at most 20 MiB. |
mask | string (binary) | Optional PNG mask upload. |
n | integer | 1–10 |
size | string | |
quality | string | |
background | string | |
input_fidelity | string | |
output_format | string | |
output_compression | integer | 0–100 |
response_format | string | |
user | string | |
stream | boolean | false |
ImageResponse
| Field | Type | Notes |
|---|---|---|
created | integer | |
datarequired | object[] | |
request_id | string (uuid) | The id to quote when contacting support about this request. |
billing | object | What this request was billed. Present on every successful response. |
Additional properties are forwarded rather than rejected.
AnthropicMessagesRequest
| Field | Type | Notes |
|---|---|---|
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
modelrequired | string | |
max_tokensrequired | integer | Required on this endpoint. |
messagesrequired | object[] | |
system | string | array | |
stream | boolean | |
stop_sequences | string[] | |
temperature | number | |
top_p | number | |
tools | object[] | |
tool_choice | object | |
thinking | object |
Additional properties are forwarded rather than rejected.
AnthropicMessagesResponse
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
typerequired | string | message |
rolerequired | string | assistant |
model | string | |
contentrequired | object[] | |
stop_reason | string | null | |
stop_sequence | string | null | |
usage | object | |
request_id | string (uuid) | The id to quote when contacting support about this request. |
billing | object | What this request was billed. Present on every successful response. |
CountTokensRequest
| Field | Type | Notes |
|---|---|---|
modelrequired | string | |
messagesrequired | object[] | |
system | string | array | |
tools | object[] |
Additional properties are forwarded rather than rejected.
CountTokensResponse
| Field | Type | Notes |
|---|---|---|
input_tokensrequired | integer |
ResponsesRequest
| Field | Type | Notes |
|---|---|---|
min_discount_percent | number | Price ceiling for this request as a minimum discount off the model's list price (see GET /v1/models pricing.list_*). Only supply priced under list × (1 − d) serves the request, and the bill is capped at that rate. With no qualifying supply the request fails with 503 `min_discount_unavailable` instead of falling back. Stripped before forwarding.0–99.99 |
zdr | boolean | Route this request only through providers that support a zero-data-retention policy. A zero-data-retention provider does not keep your prompts or responses and does not train on them. The marketplace and every unverified provider are excluded, so the price is the ZDR price shown by `GET /v1/models?zdr=true`. Adds to the account's Data Protection setting; it cannot lift it. With no qualifying supply the request fails before it is sent upstream with 503 `zdr_capacity_unavailable`. Stripped before forwarding. |
ranking | string | How the gateway orders the discounted sources that can serve this request. `discount`: cheapest first. `speed`: highest recent speed first, measured on this model from the gateway's own traffic (time to first visible output and output tokens per second, weighted towards the last hour); cost breaks ties. `balance` (default): speed score × discount. Speed and discount are NOT a trade-off: the fastest source is often also a cheap one, so `speed` can return the same route as `discount`. Fallback routes are not ranked. They are tried only when no discounted source can serve the request in time. The mode changes the order only, never which sources are eligible. Stripped before forwarding.speed, discount, balance |
modelrequired | string | |
storerequired | boolean | Must be false. This endpoint is stateless.false |
inputrequired | any | |
instructions | string | null | |
stream | boolean | |
max_output_tokens | integer | null | |
tools | ResponsesTool[] | |
tool_choice | any | |
parallel_tool_calls | boolean | |
reasoning_effort | string | Alias for reasoning.effort. Supplying conflicting values is a 400. Gemini 3 does not support `none` and returns `unsupported_reasoning_effort`.none, minimal, low, medium, high |
reasoning | object | null | Set mode and effort separately. For gpt-5.5-pro, the model ID selects Pro, so omit mode. |
text | object |
Additional properties are forwarded rather than rejected.
ResponsesFunctionTool
| Field | Type | Notes |
|---|---|---|
typerequired | string | function |
namerequired | string | length 1–∞ |
description | string | |
parameters | object | |
strict | boolean |
Additional properties are forwarded rather than rejected.
ResponsesCustomTool
A freeform/custom tool. Providers without native custom tools receive a function wrapper whose sole input is the raw custom-tool string.
| Field | Type | Notes |
|---|---|---|
typerequired | string | custom |
namerequired | string | length 1–∞ |
description | string | |
format | object |
Additional properties are forwarded rather than rejected.
ResponsesNamespaceTool
Responses Lite namespace. Function and custom entries may be mixed in one namespace.
| Field | Type | Notes |
|---|---|---|
typerequired | string | namespace |
namerequired | string | length 1–∞ |
description | string | |
toolsrequired | array |
Additional properties are forwarded rather than rejected.
ResponsesHostedTool
An optional hosted tool. Offering one is tolerated, but selecting it with tool_choice is rejected because CheaperInference cannot execute it.
| Field | Type | Notes |
|---|---|---|
typerequired | string | file_search, tool_search, web_search, web_search_preview |
Additional properties are forwarded rather than rejected.
ResponsesTool
A passthrough object: the API does not constrain its shape.
ResponsesAdditionalToolsItem
| Field | Type | Notes |
|---|---|---|
typerequired | string | additional_tools |
toolsrequired | ResponsesTool[] |
Additional properties are forwarded rather than rejected.
ResponsesInputItem
A message, reasoning item, replayed function/custom call or output, agent message, or Responses Lite additional-tools declaration.
A passthrough object: the API does not constrain its shape.
ResponsesResponse
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
objectrequired | string | response |
created_at | integer | |
statusrequired | string | completed, incomplete, failed, in_progress |
outputrequired | object[] | |
usage | object | null | |
request_id | string (uuid) | The id to quote when contacting support about this request. |
billing | object | What this request was billed. Present on every successful response. |
Additional properties are forwarded rather than rejected.
ModelList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
datarequired | Model[] | |
pricing_versionrequired | string | |
pricing_checked_at | string | null | |
pricing_updated_at | string | null |
Model
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
context_length | integer | null | Total context window in tokens; null when the provider does not publish it. |
max_output_tokens | integer | null | Published maximum output tokens; null when unknown. |
objectrequired | string | model |
owned_by | string | |
provider | string | null | |
typerequired | string | text, image, video, decision |
endpointrequired | string | Preferred endpoint for this model. Dedicated Pro models use /v1/responses; decision models use /v1/decisions. |
supported_endpointsrequired | string[] | Currently advertised endpoints after model-specific restrictions. Use this list to choose a compatible API. |
capabilitiesrequired | object | |
is_free | boolean | |
available_until | string | null | |
pricingrequired | object |
PublicModelCatalog
| Field | Type | Notes |
|---|---|---|
modelsrequired | PublicModel[] |
PublicModel
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
context_length | integer | null | |
max_output_tokens | integer | null | |
aliases | string[] | Vendor-qualified ids that also resolve to this model. |
model_typerequired | string | text, image, video, decision |
endpointrequired | string | Preferred inference endpoint for this model. |
supported_endpointsrequired | string[] | Currently advertised inference endpoints. |
input_per_millionrequired | string | |
output_per_millionrequired | string | |
cache_read_per_million | string | null | |
cache_write_per_million | string | null | |
image_output_per_million | string | null | |
media_input_unit_price | string | null | |
media_unit_price | string | null | Price per picture, for models billed per unit. Their token rates are not the price. |
media_unit | string | null | |
media_prices | map<string, string> | |
image_pricing_unit | string | token, image |
reference_input_per_million | string | null | |
reference_output_per_million | string | null | |
reference_cache_read_per_million | string | null | |
reference_cache_write_per_million | string | null | |
reference_image_output_per_million | string | null | |
reference_media_input_unit_price | string | null | |
reference_media_unit_price | string | null | |
reference_media_prices | map<string, string> | |
discount_percentrequired | string | Measured from the price actually charged against the list price, on whichever basis bills the model. `0.00` means no list price is published to compare with. |
logo_url | string | null | |
provider_name | string | null | |
primary_model_id | string | null | |
is_visible | boolean | |
supports_vision | boolean | |
supports_video | boolean | |
supports_reasoning | boolean | |
reasoning_capability_mode | string | auto, manual |
supports_streaming | boolean | |
supports_image_edit | boolean | True only after this model passes a live image-edit canary. |
is_free | boolean | |
available_until | string | null | |
input_token_price_threshold | integer | null | |
input_per_million_above_threshold | string | null | |
output_per_million_above_threshold | string | null | |
above_threshold | object | null | Conditional rates for the whole request when total prompt tokens, including cached tokens, exceed the threshold. applies_when states whether the serving supplier must also charge a long-context premium. Customer price caps still apply. |
zero_data_retention | boolean | |
zero_data_retention_route | string | null | The model's zero-data-retention tier, in every view: `discounted`, `rail_only` or `none`. Same as `pricing.zero_data_retention_route` on `GET /v1/models`.discounted, rail_only, none, null |
DetailError
The error body the /public routes answer with.
| Field | Type | Notes |
|---|---|---|
detailrequired | string |
UsageRequestList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
scoperequired | string | workspace |
datarequired | UsageRequest[] | |
has_morerequired | boolean | |
next_cursor | string | null | Opaque. Pass back as `cursor`; do not parse. |
missing_request_ids | string (uuid)[] | Present only when the request named `request_ids`: the requested ids, lower-cased, that are not in `data` because they do not exist, are not the caller's, or have passed the per-request retention window. The three are deliberately indistinguishable; use `retention_days` to tell which absences your own records explain. |
retention_days | integer | Present alongside `missing_request_ids`: how many days per-request detail is kept. Ids are never individually marked as expired, because that would reveal whether an id exists. You know when you made each request, so compare its age against this: an id older than the window is missing because the detail is no longer kept, not because the request was never served. Daily and day-by-model totals for that period remain available. |
UsageRequest
| Field | Type | Notes |
|---|---|---|
request_idrequired | string (uuid) | |
api_key_id | string | null | |
api_key_name | string | null | |
modelrequired | string | |
endpointrequired | string | |
statusrequired | string | validating, forwarding, settled, cached, failed |
prompt_tokens | integer | |
completion_tokens | integer | |
total_tokens | integer | |
billed_cost_usd | string | null | Null unless the request settled. |
provider_list_price_usd | string | null | Request-time maker/provider list-price comparison amount. |
paid_amount_usd | string | null | Customer amount paid; null unless the request settled. |
savings_usd | string | null | Customer-visible savings, always non-negative when present. |
route | string | null | Always null. Identified the supply route that served the request; withdrawn because it names our supplier. The key remains so existing clients keep a stable shape. |
serving_provider | string | null | Always null. Identified the serving provider; withdrawn for the same reason as `route`. |
retention | string | Recorded request-time retention policy; unknown for historical requests without evidence.zdr, standard, unknown |
effective_discount_percent | number | null | Effective discount against request-time list cost, including optimizations; null without comparable settled costs. |
pricing_basis | string | null | |
pricing_snapshot | object | null | Immutable customer and maker-list rates resolved at admission. |
pricing_version | string | null | |
customer_price_ceiling_usd | string | null | |
maker_list_ceiling_usd | string | null | |
requested_reasoning_effort | string | null | |
effective_reasoning_effort | string | null | |
time_to_response_headers_ms | integer | null | From our API receiving the request to the serving supplier's response headers. |
model_time_to_response_headers_ms | integer | null | From dispatch to the serving supplier to its response headers. |
time_to_first_visible_output_ms | integer | null | Like time_to_first_token_ms, but excludes reasoning-only output. Streamed requests only. |
model_time_to_first_visible_output_ms | integer | null | From dispatch to the first visible output. Streamed requests only. |
routing_overhead_ms | integer | null | From our API receiving the request to dispatch to the serving supplier, earlier failed attempts included. |
provider_attempt_count | integer | null | |
cache_reporting_state | string | null | hit, zero, unknown, not_applicable, null |
currencyrequired | string | USD |
total_latency_ms | integer | null | Latency: from our API receiving the request to the last byte of the response. |
created_atrequired | string (date-time) | |
cache_read_input_tokens | integer | null | Not recorded yet. |
cache_write_input_tokens | integer | null | Not recorded yet. |
image_output_tokens | integer | null | Not recorded yet. |
time_to_first_token_ms | integer | null | TTFT: from our API receiving the request to the first output token, reasoning included. Streamed requests only; null when not streamed. |
model_latency_ms | integer | null | Not recorded yet. |
model_time_to_first_token_ms | integer | null | From dispatch to the serving supplier to the first output token, reasoning included. routing_overhead_ms + model_time_to_first_token_ms = time_to_first_token_ms. Streamed requests only. |
generation_duration_ms | integer | null | From the first output token to the end of the response. Streamed requests only. |
output_tokens_per_second | string | null | Tokens per second: completion_tokens * 1000 / generation_duration_ms. Streamed requests only. |
completed_at | string | null | When the request settled or failed. |
billingrequired | UsageRequestBilling | |
techniquesrequired | UsageRequestTechnique[] | Cheaper Inference techniques recorded for this request. Entries recorded before outcomes were stored only ever listed applied work, so they read as `applied`. Notes, release ids and evidence are not exposed. |
attributionrequired | any | The tag written by `POST /v1/usage/attributions`, or null when the request was never attributed (also null on a deployment that has not applied the request_tags migration). |
UsageRequestBilling
The request's billing state for a client reconciling its own ledger. Derived from `status`: settled carries the settled charge; an incomplete request whose delivered partial stream was billed is also settled, with that charge, matching the stream receipt; cached, and failed with no charge, say `0.000000`; validating and forwarding (in flight, or a settlement that has not landed) are `pending` with a null amount, because the amount is not known.
| Field | Type | Notes |
|---|---|---|
statusrequired | string | settled, cached, failed, pending |
billed_cost_usdrequired | string | null | Fixed 6 decimal places, as a string. Null only while pending. |
UsageRequestTechnique
| Field | Type | Notes |
|---|---|---|
slugrequired | string | |
outcomerequired | string | applied, skipped, error |
reasonrequired | string | null | |
tokens_savedrequired | integer | min 0 |
UsageRequestAttribution
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | |
project_idrequired | string | null | |
clientrequired | string |
UsageAttributionRequest
Strict: any other field is refused with 400.
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | The client's task id. |
project_id | string | null | An opaque client-side hash identifying the project, never a readable path or URL. Optional; null when the client has none. |
clientrequired | string | keak-code |
request_idsrequired | string (uuid)[] | Request ids from the receipts. Deduplicated and lower-cased. |
UsageAttribution
| Field | Type | Notes |
|---|---|---|
objectrequired | string | usage.attribution |
task_idrequired | string (uuid) | |
project_idrequired | string | null | |
clientrequired | string | |
request_idsrequired | string (uuid)[] | |
createdrequired | integer | Tags written by this call.min 0 |
unchangedrequired | integer | Ids that already carried exactly this tag.min 0 |
UsageAttributionError
The OpenAI error envelope with the ids the caller must act on inside `error`, never at the top level.
| Field | Type | Notes |
|---|---|---|
errorrequired | object |
UsageTaskOutcomeRequest
Strict: any other field is refused with 400.
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | The task id used in the attribution. |
outcomerequired | string | completed |
clientrequired | string | keak-code |
UsageTaskOutcome
| Field | Type | Notes |
|---|---|---|
objectrequired | string | usage.task_outcome |
task_idrequired | string (uuid) | |
outcomerequired | string | completed |
createdrequired | boolean | False when the task was already recorded as completed. |
TeamSavingsSummary
Amounts are USD strings with 6 decimal places, rounded half-even, and may be negative. Rates are fractions with 6 decimal places, or null when there is nothing to divide by.
| Field | Type | Notes |
|---|---|---|
requestsrequired | integer | Charged requests: settled, cache hits and charged failures.min 0 |
actual_usdrequired | string | What those requests were billed. Cache hits count $0. |
comparable_requestsrequired | integer | Charged requests with a known list price.min 0 |
comparable_actual_usdrequired | string | |
baseline_usdrequired | string | List price of the comparable requests. |
savings_usdrequired | string | `baseline_usd - comparable_actual_usd`. May be negative. |
savings_raterequired | string | null | `savings_usd / baseline_usd`; null when the baseline is 0. |
savings_breakdownrequired | object | |
coveragerequired | object | How much of the spend the savings figure covers. |
not_comparablerequired | object | Charged requests left out of the savings because their list price is unknown. |
attributionrequired | object | Charged requests with and without a Keak Code attribution tag. |
pending_requestsrequired | integer | In flight, or not yet settled. No amount.min 0 |
failed_unbilled_requestsrequired | integer | Failed and not charged.min 0 |
tasksrequired | object | Keak Code tasks with charged requests in this period. Completed tasks are as reported by Keak Code, and their spend is only what fell inside the period. |
TeamSavingsGroup
A summary for one member, project or model.
| Field | Type | Notes |
|---|---|---|
requestsrequired | integer | Charged requests: settled, cache hits and charged failures.min 0 |
actual_usdrequired | string | What those requests were billed. Cache hits count $0. |
comparable_requestsrequired | integer | Charged requests with a known list price.min 0 |
comparable_actual_usdrequired | string | |
baseline_usdrequired | string | List price of the comparable requests. |
savings_usdrequired | string | `baseline_usd - comparable_actual_usd`. May be negative. |
savings_raterequired | string | null | `savings_usd / baseline_usd`; null when the baseline is 0. |
savings_breakdownrequired | object | |
coveragerequired | object | How much of the spend the savings figure covers. |
not_comparablerequired | object | Charged requests left out of the savings because their list price is unknown. |
attributionrequired | object | Charged requests with and without a Keak Code attribution tag. |
pending_requestsrequired | integer | In flight, or not yet settled. No amount.min 0 |
failed_unbilled_requestsrequired | integer | Failed and not charged.min 0 |
tasksrequired | object | Keak Code tasks with charged requests in this period. Completed tasks are as reported by Keak Code, and their spend is only what fell inside the period. |
keyrequired | string | null | The member's user id, the project id, or the model. Null only for the Unattributed project group. |
labelrequired | string | null | The member's email, when grouping by member and they are still a member. Null otherwise. |
unattributedrequired | boolean | True for the project group of requests with no Keak Code project tag. |
current_memberrequired | boolean | null | Grouping by member in a workspace: false for a former member, whose spend still counts. Null otherwise. |
TeamSavings
| Field | Type | Notes |
|---|---|---|
objectrequired | string | usage.team_savings |
versionrequired | integer | 1 |
generated_atrequired | string (date-time) | |
currencyrequired | string | USD |
viewerrequired | object | |
scoperequired | object | |
periodrequired | object | |
filtersrequired | object | |
group_byrequired | string | member, project, model |
baselinerequired | object | |
totalsrequired | TeamSavingsSummary | |
groupsrequired | TeamSavingsGroup[] | Highest spend first. |
group_countrequired | integer | How many groups exist.min 0 |
groups_truncatedrequired | boolean | True when more than 500 groups exist. |
TeamSavingsRequest
A request record. No prompt, response or request body.
| Field | Type | Notes |
|---|---|---|
request_idrequired | string (uuid) | |
created_atrequired | string (date-time) | |
member_idrequired | string (uuid) | The owner of the key that sent it. |
member_labelrequired | string | null | Their email while they are a member of the workspace. |
modelrequired | string | |
billing_statusrequired | string | As on `GET /v1/usage/requests`: a charged failure is `settled`.settled, cached, failed, pending |
actual_usdrequired | string | null | Null only while pending. |
baseline_usdrequired | string | null | Null when the list price is unknown or nothing was charged. |
savings_usdrequired | string | null | Null when not comparable. May be negative. |
comparablerequired | boolean | |
attributionrequired | any | |
task_outcomerequired | string | null | completed, null |
TeamSavingsRequestList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
datarequired | TeamSavingsRequest[] | |
has_morerequired | boolean | |
next_cursorrequired | string | null |
KeyTechniqueProfile
| Field | Type | Notes |
|---|---|---|
objectrequired | string | api_key.technique_profile |
api_key_idrequired | string (uuid) | |
api_key_prefixrequired | string | |
scopesrequired | string[] | |
monthly_spend_limit_usdrequired | string | null | Fixed 6 decimal places, as a string. Null when the key has no limit. |
prompt_cache_moderequired | string | The key's stored default; a workspace `prompt_cache_rule` overrides it. A request's `x-ci-prompt-cache` header overrides it too, and zero data retention forces `off`; the receipt reports the mode that applied.on, off, passthrough |
prompt_cache_mode_effectiverequired | string | The mode a request without a cache header runs under: `off` when the account has zero data retention or a pinned region, else the workspace's `prompt_cache_rule`, else `prompt_cache_mode`. A request's own `zdr` field can still force `off`; the receipt reports what applied.on, off, passthrough |
admin_policy | any | The workspace admin policy: the object for a workspace key, null for a personal key (no policy applies), and absent when this deployment's database does not have the policy yet. |
workspace_policyrequired | object | Deprecated, always false; see `admin_policy`. |
profile_matchrequired | string | null | `coding-safe-v1` when every implemented technique has a current explicit off override on this key, otherwise null.coding-safe-v1, null |
techniquesrequired | KeyTechniqueProfileEntry[] | |
contractrequired | object | |
read_atrequired | string (date-time) |
KeyTechniqueProfileEntry
| Field | Type | Notes |
|---|---|---|
idrequired | string | The technique slug. |
namerequired | string | |
availablerequired | boolean | |
unavailable_reasonrequired | string | null | not_in_catalog, not_released, null |
key_overriderequired | string | on, off, inherit |
key_override_currentrequired | boolean | null | Null when the key has no override. False when a technique release demanded re-confirmation after the override was written, so it no longer counts. |
workspacerequired | string | on, off |
workspace_explicitrequired | boolean | |
effectiverequired | boolean | What a request with this key gets. |
sourcerequired | string | key, workspace, default, unavailable |
request_header_can_force_onrequired | string[] | Request headers that can turn this technique on for one request. |
disabled_by_model_lockrequired | boolean | True for `model-routing`, which `x-ci-model-lock` always holds off. |
layerrequired | string | cheaper_inference |
locked_by_workspacerequired | boolean | The workspace admin policy locks this technique off: it never runs on this key's requests, so `effective` is false and no header can force it on. `available`, `source` and `key_override` are unchanged. |
KeyTechniqueProfileUpdate
Strict and non-empty: send at least one field; any other field is refused.
| Field | Type | Notes |
|---|---|---|
profile | string | An explicit off override for every implemented technique.coding-safe-v1 |
techniques | map<string, string> | Technique id to state, applied on top of `profile`. `on` is always refused with 403, and `inherit` while the workspace has the technique on. |
prompt_cache_mode | string | on, off, passthrough |
TaskBudget
A task budget on one API key. Money is USD as strings with exactly 6 decimal places. `limit_usd - reserved_usd - settled_usd` is what is left (`remaining_usd`, never below 0). A held request counts its worst-case charge in `reserved_usd` until it settles, then its billed amount in `settled_usd`; a request that was not billed (a failure, or no usage reported) settles at 0. On a workspace key the workspace admin policy's maximum caps `limit_usd`: an open, raise or renewal above it lands at it, and lowering the maximum lowers every open budget above it (`limit_clamped_at`).
| Field | Type | Notes |
|---|---|---|
objectrequired | string | api_key.task_budget |
idrequired | string (uuid) | |
api_key_idrequired | string (uuid) | |
task_idrequired | string (uuid) | |
clientrequired | string | keak-code |
statusrequired | string | Effective: an open budget past its lease reads `expired`.open, closed, expired |
limit_usdrequired | string | At most the workspace maximum, when the workspace admin policy sets one. |
reserved_usdrequired | string | Held by requests that have not settled. |
settled_usdrequired | string | Billed to requests that settled. |
remaining_usdrequired | string | |
reserved_countrequired | integer | Requests held, settled or not.min 0 |
settled_countrequired | integer | min 0 |
opened_atrequired | string (date-time) | |
expires_atrequired | string (date-time) | End of the lease. Renew it with PATCH. |
closed_atrequired | string | null | |
close_reasonrequired | string | null | `forced`: closed by another task with `force: true`. `admin`: closed by a workspace owner or admin.client, forced, expired, admin, null |
limit_clamped_atrequired | string | null | The last time the workspace maximum lowered this budget's limit: at an open, raise or renewal above it, or when the maximum was lowered. Null when it never was. A later raise that fits does not clear it, so it can describe an earlier limit; whether an open or update was clamped is `clamped_to_max` on that answer. |
read_atrequired | string (date-time) | The server's clock when this was read. |
replayed | boolean | On the open only: true when this task's budget with this limit (after the clamp) was already open. |
clamped_to_max | boolean | On the open and the update only: true when the limit asked for was above the workspace maximum, so `limit_usd` is the maximum instead. Never a refusal. |
requests | TaskBudgetHold[] | With `include=requests` only: up to 500 held requests, oldest first. |
requests_truncated | boolean | With `include=requests` only. |
TaskBudgetHold
One request held by the budget. Every request on the key counts, whichever client sent it; compare the ids with your own to find requests you did not send.
| Field | Type | Notes |
|---|---|---|
request_idrequired | string (uuid) | |
reserved_usdrequired | string | The request's worst-case charge. |
settled_usdrequired | string | null | What it was billed, once settled. |
statusrequired | string | reserved, settled |
created_atrequired | string (date-time) | |
settled_atrequired | string | null |
TaskBudgetList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
openrequired | any | The open, unexpired budget, or null. |
datarequired | TaskBudget[] | The 20 newest budgets, newest first, the open one included. |
admin_policy | any | The workspace admin policy: the object for a workspace key, null for a personal key (no policy applies), and absent when this deployment's database does not have the policy yet. |
TaskBudgetOpenRequest
Strict: any other field is refused with 400.
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | The client's task id. |
limit_usdrequired | string | USD, above 0 and at most 10000, at most 6 decimal places. A string, not a number. |
expires_in_srequired | integer | The lease, in seconds from now.60–86400 |
client | string | keak-code |
TaskBudgetUpdateRequest
Strict: any other field is refused with 400. Send `limit_usd`, `expires_in_s` or both.
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | Must be the budget's task. |
limit_usd | string | The new limit; not below the current one. |
expires_in_s | integer | Renew the lease to this many seconds from now.60–86400 |
TaskBudgetCloseRequest
Strict: any other field is refused with 400.
| Field | Type | Notes |
|---|---|---|
task_idrequired | string (uuid) | |
force | boolean | Close the budget even when it belongs to another task. Send only on a user's request. |
TaskBudgetError
The OpenAI error envelope, with what the client must act on inside `error`: the open budget in the way (`active`) or the budget as it is now (`budget`).
| Field | Type | Notes |
|---|---|---|
errorrequired | object |
ReceiptTechnique
| Field | Type | Notes |
|---|---|---|
slugrequired | string | |
outcomerequired | string | applied, skipped |
reasonrequired | string | null | Null when applied. `model_lock` when Model Lock held the technique off. |
tokens_savedrequired | integer | min 0 |
versionrequired | integer | null | The technique release's version when a release governed it, otherwise null. |
layerrequired | string | cheaper_inference |
ReceiptModelLock
| Field | Type | Notes |
|---|---|---|
requestedrequired | boolean | The request carried `x-ci-model-lock`. |
enforcedrequired | boolean | The lock held for this request. Never true unless requested. |
ReceiptOpening
| Field | Type | Notes |
|---|---|---|
receipt_versionrequired | integer | 1 |
phaserequired | string | opening |
request_idrequired | string (uuid) | |
api_key_idrequired | string (uuid) | |
requested_modelrequired | string | The catalog model the caller named. |
model_lockrequired | ReceiptModelLock |
ReceiptOpeningFrame
One `data:` line of compact JSON. These are the only top-level keys.
| Field | Type | Notes |
|---|---|---|
typerequired | string | response.cheaper_inference.request |
objectrequired | string | cheaper_inference.request |
cheaper_inferencerequired | ReceiptOpening |
ReceiptFinal
Never contains a key named `model` or `usage`.
| Field | Type | Notes |
|---|---|---|
receipt_versionrequired | integer | 1 |
phaserequired | string | final |
request_idrequired | string (uuid) | |
api_key_idrequired | string (uuid) | |
requested_modelrequired | string | |
served_model | string | Present only when `model_lock.enforced` is true, and then equal to `requested_model`. Omitted, with the lock still enforced, when a supplier answered with another model mid-stream: that stream is cut unbilled and no served model is stated. CI states its routing guarantee, not proof of the supplier's identity. |
model_lockrequired | ReceiptModelLock | |
completerequired | boolean | The request completed; false when the stream ended incomplete. |
billingrequired | object | |
tokensrequired | any | Null when usage is unknown (or a count is not a safe non-negative integer). |
prompt_cache_moderequired | string | null | The prompt-cache mode that applied: the request header, else the key's default; zero data retention forces `off`.on, off, passthrough, null |
techniquesrequired | ReceiptTechnique[] | Each Cheaper Inference technique applied to this request, plus a `model-routing` entry skipped with reason `model_lock` only when something (the key, the workspace, `x-ci-route`, a trial or a private release) had routing on. With routing off there is no skipped entry, so a client must not require one. |
warningsrequired | string[] |
ReceiptFinalFrame
One `data:` line of compact JSON. These are the only top-level keys.
| Field | Type | Notes |
|---|---|---|
typerequired | string | response.cheaper_inference.receipt |
objectrequired | string | cheaper_inference.receipt |
cheaper_inferencerequired | ReceiptFinal |
DocSectionList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
queryrequired | string | |
datarequired | object[] |
PricingChangeList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
pricing_versionrequired | string | |
pricing_checked_at | string (date-time) | |
pricing_updated_at | string (date-time) | |
datarequired | object[] | |
has_morerequired | boolean | |
next_cursor | string |
PricingChangeRates
Customer-facing rates on one side of a change. Null means the rate was not quoted at that point, which is different from zero.
| Field | Type | Notes |
|---|---|---|
input_per_million | number | string | null | |
output_per_million | number | string | null | |
media_unit_price | number | string | null | |
cache_read_per_million | number | string | null | |
cache_write_per_million | number | string | null |
UsageDaily
| Field | Type | Notes |
|---|---|---|
objectrequired | string | usage.daily |
scoperequired | string | workspace |
currencyrequired | string | USD |
start_at | string (date-time) | |
end_at | string (date-time) | |
timezone_offset_minutes | integer | |
total_requests | integer | |
settled_requests | integer | |
spend_usdrequired | string | |
prompt_tokensrequired | integer | Settled prompt tokens. Already includes any tokens served from the prompt cache. |
cached_tokensrequired | integer | The share of prompt_tokens the upstream served from its prompt cache. |
cache_reported_request_countrequired | integer | Settled requests whose upstream reported cache usage at all. Compare with settled_requests to see how much of the window cache_hit_pct speaks for. |
cache_hit_pctrequired | number | null | Percent of prompt tokens served from cache, over the cache-reporting requests only. Null when no upstream reported cache usage indicates an unknown rate, which is not the same as a reported 0%. |
daily_spendrequired | object[] |
FeedbackRequest
| Field | Type | Notes |
|---|---|---|
request_idrequired | string (uuid) | |
scorerequired | integer | 1–5 |
comment | string | null |
FeedbackResponse
| Field | Type | Notes |
|---|---|---|
okrequired | boolean |
Account
| Field | Type | Notes |
|---|---|---|
objectrequired | string | account |
email | string | null | |
workspace_name | string | null | Null for a personal account. |
role | string | null | |
funded_by_owner | boolean | True when this member's usage is paid from the workspace owner's wallet. |
active_key_countrequired | integer | |
last_key_use_at | string | null | |
auto_recharge_enabled | boolean | |
enabled_technique_countrequired | integer |
SecurityStrikeRequest
| Field | Type | Notes |
|---|---|---|
toolrequired | string | The MCP tool that was called.length 0–80 |
kindrequired | string | What was refused. `injection`, `escalation` and `policy` are refusals the MCP server issued itself and each count as a strike; `unauthorized` (401) and `forbidden` (403) are API denials and count once per distinct `detail`.injection, escalation, policy, unauthorized, forbidden |
detailrequired | string | A short stable code for the reason. Never the offending text.length 0–200 |
SecurityStanding
| Field | Type | Notes |
|---|---|---|
objectrequired | string | security.standing |
tierrequired | string | clear, warn, blocked |
strikesrequired | integer | Distinct strikes in the current window. This is what the tiers compare against. |
attemptsrequired | integer | Every denied call in the window, duplicates included. |
remainingrequired | integer | Strikes left before the block. 0 while blocked. |
warn_afterrequired | integer | |
block_afterrequired | integer | The daily limit. |
window_hoursrequired | integer | |
blocked_untilrequired | string | null | When a timed block lifts. Null when not blocked, and null when the block is permanent. See `permanent`. |
permanentrequired | boolean | True for a block with no expiry: the daily limit was reached again within the repeat window of an earlier block. Only an admin lifts it. |
prior_blocksrequired | integer | Earlier blocks on record that ran their course. One or more means the next block is permanent. A block an admin lifted does not count. |
ban_idrequired | string | null | |
suspension_idrequired | string | null | |
report_urlrequired | string (uri) | Where to appeal. |
reportedrequired | boolean | True when this call filed the automatic support report for the account. |
AccountBalance
| Field | Type | Notes |
|---|---|---|
objectrequired | string | account.balance |
currencyrequired | string | USD |
balance_usdrequired | number | |
available_usdrequired | number | Balance minus what is held against requests in flight. This, not balance_usd, is what can be spent right now. |
reserved_usdrequired | number | |
auto_recharge_enabled | boolean | |
threshold_usd | number | null | |
recharge_amount_usd | number | null | |
card | string | null | |
workspace_name | string | null | |
funded_by_owner | boolean |
AccountUsage
Rolled up over a window. Distinct from /v1/usage/daily, which is the per-day series.
| Field | Type | Notes |
|---|---|---|
objectrequired | string | account.usage |
currencyrequired | string | USD |
daysrequired | integer | |
request_countrequired | integer | |
billed_usdrequired | number | |
saved_usdrequired | number | List price minus what was billed. |
list_usd | number | |
savings_percent | number | null | |
top_models | object[] |
AccountSavings
| Field | Type | Notes |
|---|---|---|
objectrequired | string | account.savings |
currencyrequired | string | USD |
windowsrequired | object[] | One entry each for the last 24 hours, 7 days and 30 days. |
experimentsrequired | ExperimentSummary[] | |
techniquesrequired | Technique[] |
ExperimentSummary
| Field | Type | Notes |
|---|---|---|
idrequired | string (uuid) | |
namerequired | string | |
statusrequired | string | |
dimension | string | What the experiment varies: prompt, model or technique. |
request_countrequired | integer | |
avg_cost_usd | number | |
avg_saved_usd | number | |
feedback_avg | number | null | |
feedback_count | integer |
ExperimentList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
datarequired | ExperimentSummary[] |
Experiment
| Field | Type | Notes |
|---|---|---|
objectrequired | string | experiment |
idrequired | string (uuid) | |
namerequired | string | |
statusrequired | string | |
dimension | string | |
created_at | string | null | |
request_count | integer | |
avg_cost_usd | number | |
avg_saved_usd | number | |
feedback_avg | number | null | |
feedback_count | integer | |
variantsrequired | object[] |
Technique
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
namerequired | string | |
category | string | |
tier | integer | 1 deterministic, 2 statistical, 3 model-touching. |
status | string | enabled, preview, deprecated |
available | boolean | Whether PATCH will accept it. False for preview and deprecated entries. |
enabledrequired | boolean | What the proxy will actually do for this account. Every implemented technique is off by default, so this is false until the account opts in. |
explicitly_enabled | boolean | null | The stored choice, or null when the account has never set one. |
savings_summary | string | |
savings_min_pct | number | null | |
savings_max_pct | number | null | |
quality_risk | string | null |
TechniqueList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
datarequired | Technique[] |
TechniqueUpdateRequest
| Field | Type | Notes |
|---|---|---|
enabledrequired | boolean |
TechniqueUpdateResponse
| Field | Type | Notes |
|---|---|---|
objectrequired | string | technique |
idrequired | string | |
name | string | |
enabledrequired | boolean | |
changedrequired | boolean | False when it was already in the requested state. |
ApiKeySummary
| Field | Type | Notes |
|---|---|---|
idrequired | string (uuid) | |
namerequired | string | |
prefixrequired | string | The first 18 characters. The rest is never recoverable. |
scopesrequired | string[] | |
organization_id | string | null | |
created_at | string | null | |
last_used_at | string | null | |
expires_at | string | null | |
revoked_at | string | null |
ApiKeyList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
datarequired | ApiKeySummary[] |
ApiKeyCreateRequest
| Field | Type | Notes |
|---|---|---|
name | string | length 0–64 |
organization_id | string | null | |
scopes | string[] | Defaults to inference and usage:read. |
allowed_models | string[] | |
allowed_ip_cidrs | string[] | |
expires_at | string | null | |
rate_limit_per_minute | integer | null | |
daily_request_limit | integer | null | |
concurrency_limit | integer | null | |
monthly_spend_limit_usd | number | null |
ApiKeyCreated
| Field | Type | Notes |
|---|---|---|
objectrequired | string | api_key |
id | string | null | |
namerequired | string | |
prefixrequired | string | |
scopes | string[] | |
keyrequired | string | The plaintext key. Also available to its owner through the dashboard's copy action. |
ApiKeyRevoked
| Field | Type | Notes |
|---|---|---|
objectrequired | string | api_key.revoked |
idrequired | string (uuid) | |
name | string | |
prefix | string |
Channel
| Field | Type | Notes |
|---|---|---|
object | string | channel |
idrequired | string (uuid) | |
platformrequired | string | |
workspace_name | string | null | |
integration_active | boolean | |
eventsrequired | string[] | |
report_frequency | string | null | daily, weekly, monthly, off, null |
report_hour | integer | null | Hour of day, UTC. |
ChannelList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
datarequired | Channel[] | |
available_eventsrequired | object[] | The valid ids for the `events` field on PATCH, with their labels. |
ChannelUpdateRequest
Both fields are optional; an absent one is left alone. `events: []` unsubscribes from everything, which is different from omitting the field.
| Field | Type | Notes |
|---|---|---|
report_frequency | string | daily, weekly, monthly, off |
events | string[] |
ChannelReportResponse
| Field | Type | Notes |
|---|---|---|
objectrequired | string | channel.report |
sentrequired | boolean | False with a `message` when there was no activity in the window. The request succeeded, but there was nothing to send. |
cadence | string | |
window_label | string | |
message | string |
ModelSupply
| Field | Type | Notes |
|---|---|---|
object | string | model.supply |
model | string | |
min_discount_percent | number | null | |
zdr | boolean | |
list_input_per_million | string | null | |
list_output_per_million | string | null | |
max_input_per_million | string | null | |
max_output_per_million | string | null | |
supply_buffer_percent | number | |
candidate_count | integer | |
candidates | string[] | Opaque per-response aliases: `primary` for the marketplace, `network_a`, `network_b`, ... for the others. Suppliers are never named. |
primary_market_offers | integer | |
primary_market_min_discount_percent | integer | |
note | string |
ModelComparison
| Field | Type | Notes |
|---|---|---|
objectrequired | string | model.comparison |
currencyrequired | string | USD |
prompt_tokens | integer | |
completion_tokens | integer | |
requests | integer | |
datarequired | object[] | Cheapest first. Ids that could not be resolved sort last with found:false. |
TopupRequest
| Field | Type | Notes |
|---|---|---|
amount_usdrequired | number | 5–1000 |
TopupResponse
Creates a Stripe Checkout session and returns its link. Nothing is charged here. The customer completes the payment on Stripe's own page.
| Field | Type | Notes |
|---|---|---|
objectrequired | string | billing.topup |
amount_usdrequired | number | |
checkout_urlrequired | string (uri) | |
session_id | string | |
min_usd | number | |
max_usd | number |
WorkspaceAdminPolicy
Five settings on a workspace that its owners and admins edit and that bind everyone in it, owners and admins included. Personal keys are outside it.
| Field | Type | Notes |
|---|---|---|
objectrequired | string | workspace.admin_policy |
organization_idrequired | string (uuid) | |
versionrequired | integer | Goes up by one on every save. Send it back as `expected_version` to change the policy.min 0 |
task_limitrequired | object | |
techniques_locked_offrequired | string[] | Technique ids that never run on this workspace's traffic, whatever a key, the workspace setting, a header or an experiment asks; a receipt lists them as `skipped` with reason `workspace_policy`. Sorted and distinct. |
prompt_cache_rulerequired | string | null | Overrides the `x-ci-prompt-cache` header and every key's default. Zero data retention still forces `off`. Null: no rule.on, off, passthrough, null |
updated_atrequired | string | null | Null until the policy is first saved. |
WorkspaceAdminPolicyView
The workspace admin policy, and whether the caller may change it.
| Field | Type | Notes |
|---|---|---|
objectrequired | string | workspace.admin_policy |
organization_idrequired | string (uuid) | |
versionrequired | integer | Goes up by one on every save. Send it back as `expected_version` to change the policy.min 0 |
task_limitrequired | object | |
techniques_locked_offrequired | string[] | Technique ids that never run on this workspace's traffic, whatever a key, the workspace setting, a header or an experiment asks; a receipt lists them as `skipped` with reason `workspace_policy`. Sorted and distinct. |
prompt_cache_rulerequired | string | null | Overrides the `x-ci-prompt-cache` header and every key's default. Zero data retention still forces `off`. Null: no rule.on, off, passthrough, null |
updated_atrequired | string | null | Null until the policy is first saved. |
can_editrequired | boolean | True for a workspace owner or admin. |
WorkspaceAdminPolicySaved
The workspace admin policy as saved.
| Field | Type | Notes |
|---|---|---|
objectrequired | string | workspace.admin_policy |
organization_idrequired | string (uuid) | |
versionrequired | integer | Goes up by one on every save. Send it back as `expected_version` to change the policy.min 0 |
task_limitrequired | object | |
techniques_locked_offrequired | string[] | Technique ids that never run on this workspace's traffic, whatever a key, the workspace setting, a header or an experiment asks; a receipt lists them as `skipped` with reason `workspace_policy`. Sorted and distinct. |
prompt_cache_rulerequired | string | null | Overrides the `x-ci-prompt-cache` header and every key's default. Zero data retention still forces `off`. Null: no rule.on, off, passthrough, null |
updated_atrequired | string | null | Null until the policy is first saved. |
clamped_task_budget_countrequired | integer | How many open task budgets a lowered `task_limit_max_usd` lowered to it.min 0 |
WorkspaceAdminPolicyInput
The whole policy, every field present (null for a setting that is not set). Strict: any other field is refused with 400.
| Field | Type | Notes |
|---|---|---|
expected_versionrequired | integer | The `version` that was read. A policy saved since then answers 409.min 0 |
task_limit_default_usdrequired | string | null | USD as a string, above 0 and at most 10000, at most 6 decimal places; not above the maximum. |
task_limit_max_usdrequired | string | null | USD as a string, above 0 and at most 10000, at most 6 decimal places. |
task_budget_requiredrequired | boolean | |
techniques_locked_offrequired | string[] | Distinct technique ids from the catalog. |
prompt_cache_rulerequired | string | null | on, off, passthrough, null |
WorkspaceAdminPolicyConflict
The OpenAI error envelope, with the policy as it is now inside `error`.
| Field | Type | Notes |
|---|---|---|
errorrequired | object |
AdminTaskBudget
A task budget in the workspace, as an owner or admin sees it: the member's own view plus whose it is, on which key, and who closed it.
| Field | Type | Notes |
|---|---|---|
objectrequired | string | api_key.task_budget |
idrequired | string (uuid) | |
api_key_idrequired | string (uuid) | |
task_idrequired | string (uuid) | |
clientrequired | string | keak-code |
statusrequired | string | Effective: an open budget past its lease reads `expired`.open, closed, expired |
limit_usdrequired | string | At most the workspace maximum, when the workspace admin policy sets one. |
reserved_usdrequired | string | Held by requests that have not settled. |
settled_usdrequired | string | Billed to requests that settled. |
remaining_usdrequired | string | |
reserved_countrequired | integer | Requests held, settled or not.min 0 |
settled_countrequired | integer | min 0 |
opened_atrequired | string (date-time) | |
expires_atrequired | string (date-time) | End of the lease. Renew it with PATCH. |
closed_atrequired | string | null | |
close_reasonrequired | string | null | `forced`: closed by another task with `force: true`. `admin`: closed by a workspace owner or admin.client, forced, expired, admin, null |
limit_clamped_atrequired | string | null | The last time the workspace maximum lowered this budget's limit: at an open, raise or renewal above it, or when the maximum was lowered. Null when it never was. A later raise that fits does not clear it, so it can describe an earlier limit; whether an open or update was clamped is `clamped_to_max` on that answer. |
read_atrequired | string (date-time) | The server's clock when this was read. |
requests | TaskBudgetHold[] | With `include=requests` only: up to 500 held requests, oldest first. |
requests_truncated | boolean | With `include=requests` only. |
user_idrequired | string (uuid) | The member who opened it. |
member_emailrequired | string | null | |
member_rolerequired | string | null | Null once the member has left the workspace.owner, admin, member, viewer, null |
api_key_prefixrequired | string | null | |
api_key_namerequired | string | null | |
closed_by_user_idrequired | string | null | The owner or admin who closed it (`close_reason: admin`), else null. |
AdminTaskBudgetList
| Field | Type | Notes |
|---|---|---|
objectrequired | string | list |
datarequired | AdminTaskBudget[] | Newest first. |
has_morerequired | boolean | More budgets match than `limit` returned. |