{"openapi":"3.1.0","info":{"title":"RelaySwitch API","version":"2026-10-19","summary":"One key and one base URL for Claude, GPT, Gemini and the open-weight models in the OpenAI, Anthropic and Gemini wire formats, plus image, video, speech and 3D models.","description":"Send the request your SDK already sends, to `https://api.relayswitch.io/v1` instead of the developer's URL. The proxy changes only the credential, `stream_options.include_usage` on OpenAI-format streams that did not set it, a `top_k` of 0 or less (off) which it leaves out, and the model id when the request named one of your aliases.\n\nAuthentication: `Authorization: Bearer sk-relay-live-...`, or the header your SDK sends (`x-api-key` for the Anthropic SDK, `x-goog-api-key` for the Gemini SDK). Exactly one key per request, never in the URL.\n\nModel ids are enumerated by GET /v1/catalog (chat, embedding, moderation, rerank and transcription models; each entry's `type` and `endpoints` say which route takes it) and GET /v1/catalog/media (media), with prices, and by GET /v1/models for what a given key may call. They are not listed in this document.\n\nErrors: every error is a JSON object whose `error` carries a `code` and a `message`. OpenAI-format routes and every route outside the chat formats add `type` and `param` (the `Error` schema); the Anthropic format answers in Anthropic's error shape with the code appended to the message, and the Gemini format in Google's. Every error carries `x-uplink-request-id`.\n\nFallbacks: `models: [\"a\", \"b\"]` in an OpenAI- or Anthropic-format body names up to 5 models tried in order after `model` (or alone, its first entry being the model). The next model is tried only once every route of the one before has failed before a byte reached you, so a stream falls back only before its first byte. The answer's `model` and `x-uplink-model` name the model that answered, which is the one billed, at its own price. Every entry obeys the key's allowlist. A key's `fallback_models` is used when a request names no `models`.\n\nPresets: `model: \"@preset/<name>\"` (also `@<name>`, `<model>@preset/<name>`, or `preset: \"@preset/<name>\"` beside a model) runs one of your organization's presets, managed at /v1/presets: its model, fallbacks, default params, system prompt (prepended to yours, or replacing it) and tools. Your request's own fields win; your tools replace the preset's tools of the same name and the rest are appended.\n\nEvery response carries `x-uplink-request-id`; quote it when you write in. Chat responses add `x-uplink-model` and, when they apply, the headers under `components.headers`.\n\nPaying per request without a key (x402): POST /v1/chat/completions, /v1/messages, /v1/responses, /v1/embeddings or /v1/search with no credential header answers 402 with an x402 PaymentRequired object (protocol version 2) as the body and base64 in the PAYMENT-REQUIRED header. `accepts[0]` is scheme `checkout`: `payTo` is a one-time crypto checkout link (USDT, USDC, BTC, ETH, SOL and 13 more), `amount` the dollars to pay (the hold a key would be held for this request, rounded up to whole dollars, at least $1; the hold itself is `extra.request_usd`: the input at 4 bytes a token plus `max_tokens`, 16,384 when the request sets none, at the model's catalog price, or one search's fee on /v1/search), `extra.payment_id` the id of the payment. Pay there, then send the same request again with `X-PAYMENT: <payment_id>` (or PAYMENT-SIGNATURE carrying a base64 x402 PaymentPayload whose `payload.payment_id` is the id). Until the payment confirms the answer is 402 x402_payment_pending with the same quote; once it has, the request runs with the payment id as its end user and its own rate limit, and the answer adds PAYMENT-RESPONSE (base64 `{ success, transaction, network, payer }`) beside x-uplink-charge and the signed receipt. Every request sent with the id spends the payment down at the catalog price, and what the last one leaves stays with the service; a request the remainder cannot hold is a 402 x402_payment_spent with a fresh checkout. `stream` must be false. The model must be one id from GET /v1/catalog (no aliases, presets or `auto`). GET /v1/catalog `x402` carries the scheme and the smallest payment, and is null where paying per request is off.","contact":{"url":"https://relayswitch.io/contact","email":"hello@relayswitch.io"}},"servers":[{"url":"https://api.relayswitch.io","description":"Production"}],"tags":[{"name":"chat","description":"Model requests in the OpenAI, Anthropic and Gemini wire formats."},{"name":"models","description":"What a key can call, and the public catalog with prices."},{"name":"pricing","description":"Cost before sending."},{"name":"media","description":"Images, video, speech and 3D."},{"name":"audio","description":"Speech to text: transcription and translation into English."},{"name":"batch","description":"Batch API: upload a JSONL file of requests, run it within 24 hours, download the answers."},{"name":"receipts","description":"What a finished request cost, and the signed proof of it."},{"name":"keys","description":"The key API: hand out keys that each carry their own prepaid budget, and manage them with an admin key."},{"name":"sessions","description":"Session traces: requests grouped by the x-session-id they carried, and the usage log as CSV. An admin key or a console sign-in."},{"name":"exports","description":"Log export: every request, host-free, to your own webhook, Langfuse or any OpenTelemetry (OTLP/HTTP) backend. An admin key or a console sign-in."},{"name":"guardrails","description":"Opt-in checks on your own traffic: blocked patterns, prompt injection detection and a tool policy, per key or as the organization's default."},{"name":"sign-in","description":"Sign in with RelaySwitch: an app gets a key for its user through OAuth with PKCE."},{"name":"mcp","description":"The organization MCP gateway: your MCP servers behind one endpoint, on your API key."},{"name":"webhooks","description":"Account-event webhooks: balance.low, balance.empty, key.budget_reached, key.limit_reached, topup.credited, payment.failed, incident.opened and incident.resolved, signed to up to three HTTPS URLs."},{"name":"search","description":"Web search on its own: the chat tools' web_search as one request, billed per search."},{"name":"invoices","description":"Invoices: a PDF per credited top-up, numbered INV-<year>-<n> per organization, with the billing details the organization saved. An admin key or a console sign-in."},{"name":"presets","description":"Saved presets: a model, fallbacks, params, a system prompt and tools under one name, called as `model: \"@preset/<name>\"`."},{"name":"status","description":"Public feeds: service status with 90 days of incidents, status emails by subscription, and the daily real-model check."},{"name":"meta","description":"This document."}],"security":[{"bearer":[]},{"apiKey":[]},{"googleApiKey":[]}],"paths":{"/v1/chat/completions":{"post":{"operationId":"createChatCompletion","summary":"Chat completion, OpenAI format","description":"OpenAI's Chat Completions request, passed through to the model. With `stream: true` the answer is `text/event-stream` in the same format. A stream's headers leave before its tokens exist, so x-uplink-charge, x-uplink-list-cost, x-uplink-saved and x-uplink-receipt are not on it; GET /v1/requests/{request_id} has them once the request settles. Each usage event in the stream carries `usage.cost`, the charge so far in USD; the last one (OpenAI's usage chunk when `stream_options.include_usage` is set, Anthropic's `message_delta`) is the whole charge. Server tools: declare `web_search` (OpenAI `{\"type\":\"web_search\"}`, `web_search_preview`, chat's `web_search_options`, Anthropic `web_search_20250305`), `web_fetch` (`{\"type\":\"web_fetch\"}`, Anthropic `web_fetch_20250910`), `code_execution` (`code_interpreter`, Anthropic `code_execution_20250825`; bash, javascript or python; `session_id` or `container` keeps a sandbox's files between requests) or remote MCP (OpenAI `{\"type\":\"mcp\",\"server_label\",\"server_url\",\"headers\",\"allowed_tools\",\"require_approval\":\"never\"}`, Anthropic `mcp_servers` with `mcp_toolset`), or the same as `uplink:web_search`, `uplink:web_fetch`, `uplink:code_execution`, `uplink:mcp`, and the gateway runs them inside this one request: when the model calls one, the tool runs, its result goes back to the model, and the model is asked again, up to `max_tool_rounds` (1 to 8, default 8; or the x-uplink-max-tool-rounds header), then once more without tools so the answer ends in text. Each model round is billed and receipted as its own request; tool fees are per call (GET /v1/catalog `server_tools`), held before each batch and refused with a 402 when the balance, the key's budget or its monthly limit cannot cover them. Sources the answer links to come back as native citations (`annotations` with `url_citation`; Anthropic `citations` and `server_tool_use` / `*_tool_result` blocks; Responses `web_search_call`, `mcp_call`, `code_interpreter_call` items). With `stream: true` the tool rounds run first and the final answer is then sent as this format's own events; the receipt header is left off the stream (GET /v1/requests/{request_id}/receipt has it). A tool error goes back to the model as the tool's result, never as an HTTP error. Web search without a model: POST /v1/search.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"},{"$ref":"#/components/parameters/x-session-id"},{"$ref":"#/components/parameters/x-customer-id"},{"$ref":"#/components/parameters/x-uplink-redact"},{"$ref":"#/components/parameters/x-uplink-cache"},{"$ref":"#/components/parameters/x-uplink-cache-ttl"},{"$ref":"#/components/parameters/x-uplink-cache-clear"},{"$ref":"#/components/parameters/x-uplink-max-tool-rounds"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionRequest"}}}},"responses":{"200":{"description":"The model's answer in the OpenAI chat completion shape, as its route returns it.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"application/json":{"schema":{"type":"object","description":"OpenAI chat completion object.","additionalProperties":true}},"text/event-stream":{"schema":{"type":"string","description":"Server-sent events, each `data:` line an OpenAI chat completion chunk, ending with `data: [DONE]`."}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/completions":{"post":{"operationId":"createCompletion","summary":"Text completion, OpenAI format","description":"OpenAI's legacy Completions request, passed through to the model. With `stream: true` the answer is `text/event-stream` in the same format. A stream's headers leave before its tokens exist, so x-uplink-charge, x-uplink-list-cost, x-uplink-saved and x-uplink-receipt are not on it; GET /v1/requests/{request_id} has them once the request settles. Each usage event in the stream carries `usage.cost`, the charge so far in USD; the last one (OpenAI's usage chunk when `stream_options.include_usage` is set, Anthropic's `message_delta`) is the whole charge.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"},{"$ref":"#/components/parameters/x-session-id"},{"$ref":"#/components/parameters/x-customer-id"},{"$ref":"#/components/parameters/x-uplink-redact"},{"$ref":"#/components/parameters/x-uplink-cache"},{"$ref":"#/components/parameters/x-uplink-cache-ttl"},{"$ref":"#/components/parameters/x-uplink-cache-clear"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"OpenAI completion request.","required":["model"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"prompt":{"type":["string","array"]},"stream":{"type":"boolean","description":"Stream the answer as server-sent events."}},"additionalProperties":true}}}},"responses":{"200":{"description":"The model's answer in the OpenAI completion shape, as its route returns it.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"application/json":{"schema":{"type":"object","description":"OpenAI chat completion object.","additionalProperties":true}},"text/event-stream":{"schema":{"type":"string","description":"Server-sent events, each `data:` line an OpenAI chat completion chunk, ending with `data: [DONE]`."}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/responses":{"post":{"operationId":"createResponse","summary":"Response, OpenAI Responses format","description":"OpenAI's Responses request, passed through to the model. With `stream: true` the answer is `text/event-stream` in the same format. A stream's headers leave before its tokens exist, so x-uplink-charge, x-uplink-list-cost, x-uplink-saved and x-uplink-receipt are not on it; GET /v1/requests/{request_id} has them once the request settles. Each usage event in the stream carries `usage.cost`, the charge so far in USD; the last one (OpenAI's usage chunk when `stream_options.include_usage` is set, Anthropic's `message_delta`) is the whole charge. Server tools: declare `web_search` (OpenAI `{\"type\":\"web_search\"}`, `web_search_preview`, chat's `web_search_options`, Anthropic `web_search_20250305`), `web_fetch` (`{\"type\":\"web_fetch\"}`, Anthropic `web_fetch_20250910`), `code_execution` (`code_interpreter`, Anthropic `code_execution_20250825`; bash, javascript or python; `session_id` or `container` keeps a sandbox's files between requests) or remote MCP (OpenAI `{\"type\":\"mcp\",\"server_label\",\"server_url\",\"headers\",\"allowed_tools\",\"require_approval\":\"never\"}`, Anthropic `mcp_servers` with `mcp_toolset`), or the same as `uplink:web_search`, `uplink:web_fetch`, `uplink:code_execution`, `uplink:mcp`, and the gateway runs them inside this one request: when the model calls one, the tool runs, its result goes back to the model, and the model is asked again, up to `max_tool_rounds` (1 to 8, default 8; or the x-uplink-max-tool-rounds header), then once more without tools so the answer ends in text. Each model round is billed and receipted as its own request; tool fees are per call (GET /v1/catalog `server_tools`), held before each batch and refused with a 402 when the balance, the key's budget or its monthly limit cannot cover them. Sources the answer links to come back as native citations (`annotations` with `url_citation`; Anthropic `citations` and `server_tool_use` / `*_tool_result` blocks; Responses `web_search_call`, `mcp_call`, `code_interpreter_call` items). With `stream: true` the tool rounds run first and the final answer is then sent as this format's own events; the receipt header is left off the stream (GET /v1/requests/{request_id}/receipt has it). A tool error goes back to the model as the tool's result, never as an HTTP error. Web search without a model: POST /v1/search.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"},{"$ref":"#/components/parameters/x-session-id"},{"$ref":"#/components/parameters/x-customer-id"},{"$ref":"#/components/parameters/x-uplink-redact"},{"$ref":"#/components/parameters/x-uplink-cache"},{"$ref":"#/components/parameters/x-uplink-cache-ttl"},{"$ref":"#/components/parameters/x-uplink-cache-clear"},{"$ref":"#/components/parameters/x-uplink-max-tool-rounds"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"OpenAI Responses request.","required":["model"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"input":{"type":["string","array"]},"stream":{"type":"boolean","description":"Stream the answer as server-sent events."}},"additionalProperties":true}}}},"responses":{"200":{"description":"The model's answer in the OpenAI Responses shape, as its route returns it.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"application/json":{"schema":{"type":"object","description":"OpenAI chat completion object.","additionalProperties":true}},"text/event-stream":{"schema":{"type":"string","description":"Server-sent events, each `data:` line an OpenAI chat completion chunk, ending with `data: [DONE]`."}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/embeddings":{"post":{"operationId":"createEmbedding","summary":"Embeddings, OpenAI format","description":"OpenAI's Embeddings request, passed through to the model.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-app"},{"$ref":"#/components/parameters/x-uplink-cache"},{"$ref":"#/components/parameters/x-uplink-cache-ttl"},{"$ref":"#/components/parameters/x-uplink-cache-clear"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"OpenAI embeddings request.","required":["model","input"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"input":{"type":["string","array"]}},"additionalProperties":true}}}},"responses":{"200":{"description":"OpenAI embedding list, as the model's route returns it.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/moderations":{"post":{"operationId":"createModeration","summary":"Moderation, OpenAI format","description":"OpenAI's moderation request, passed through to a moderation model (GET /v1/catalog, `type` moderation). With no `model` the request goes to text-moderation-latest. The answer is OpenAI's moderation object: `results` with `flagged`, `categories` and `category_scores`. Billed per input token at the model's catalog price; the answer carries no token count, so the count is the request's size over four.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-app"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"OpenAI moderation request.","required":["input"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"input":{"type":["string","array"]}},"additionalProperties":true}}}},"responses":{"200":{"description":"OpenAI moderation object, as the model's route returns it.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/rerank":{"post":{"operationId":"createRerank","summary":"Rerank documents against a query","description":"Cohere's and Jina's rerank shape: the documents scored for relevance to the query, best first. Rerank models are listed in GET /v1/catalog with `type` rerank. Billed per input token at the catalog price: the query counts once beside every document (a cross-encoder reads each pair), about four characters a token; `usage` gives the count. A failed request is not charged.","tags":["chat"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","query","documents"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"query":{"type":"string","description":"What the documents are ranked against."},"documents":{"type":"array","description":"Up to 1,000 documents: strings, or objects with a `text` field.","items":{"type":["string","object"]}},"top_n":{"type":"integer","description":"Return only the best n. Default: every document."},"return_documents":{"type":"boolean","description":"Include each document's text in its result."}}}}}},"responses":{"200":{"description":"The ranked documents.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-charge-usd":{"$ref":"#/components/headers/x-uplink-charge-usd"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RerankResponse"}}}},"400":{"description":"invalid_request: no model, query or documents, more than 1,000 documents, or the model is not a rerank model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached, monthly_limit_reached or key_budget_exhausted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied, admin_key or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists the rerank models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 2 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited, or upstream_rate_limited when the model is busy. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model could not rerank right now. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/messages":{"post":{"operationId":"createMessage","summary":"Message, Anthropic format","description":"Anthropic's Messages request, passed through to the model. The Anthropic SDK sends `x-api-key` and `anthropic-version`; both pass. With `stream: true` the answer is `text/event-stream` in the same format. A stream's headers leave before its tokens exist, so x-uplink-charge, x-uplink-list-cost, x-uplink-saved and x-uplink-receipt are not on it; GET /v1/requests/{request_id} has them once the request settles. Each usage event in the stream carries `usage.cost`, the charge so far in USD; the last one (OpenAI's usage chunk when `stream_options.include_usage` is set, Anthropic's `message_delta`) is the whole charge. On this format a large system prompt that many accounts send may get a cache breakpoint (crowd cache); `x-uplink-crowd-cache: off` leaves the request alone. Server tools: declare `web_search` (OpenAI `{\"type\":\"web_search\"}`, `web_search_preview`, chat's `web_search_options`, Anthropic `web_search_20250305`), `web_fetch` (`{\"type\":\"web_fetch\"}`, Anthropic `web_fetch_20250910`), `code_execution` (`code_interpreter`, Anthropic `code_execution_20250825`; bash, javascript or python; `session_id` or `container` keeps a sandbox's files between requests) or remote MCP (OpenAI `{\"type\":\"mcp\",\"server_label\",\"server_url\",\"headers\",\"allowed_tools\",\"require_approval\":\"never\"}`, Anthropic `mcp_servers` with `mcp_toolset`), or the same as `uplink:web_search`, `uplink:web_fetch`, `uplink:code_execution`, `uplink:mcp`, and the gateway runs them inside this one request: when the model calls one, the tool runs, its result goes back to the model, and the model is asked again, up to `max_tool_rounds` (1 to 8, default 8; or the x-uplink-max-tool-rounds header), then once more without tools so the answer ends in text. Each model round is billed and receipted as its own request; tool fees are per call (GET /v1/catalog `server_tools`), held before each batch and refused with a 402 when the balance, the key's budget or its monthly limit cannot cover them. Sources the answer links to come back as native citations (`annotations` with `url_citation`; Anthropic `citations` and `server_tool_use` / `*_tool_result` blocks; Responses `web_search_call`, `mcp_call`, `code_interpreter_call` items). With `stream: true` the tool rounds run first and the final answer is then sent as this format's own events; the receipt header is left off the stream (GET /v1/requests/{request_id}/receipt has it). A tool error goes back to the model as the tool's result, never as an HTTP error. Web search without a model: POST /v1/search.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"},{"$ref":"#/components/parameters/x-session-id"},{"$ref":"#/components/parameters/x-customer-id"},{"$ref":"#/components/parameters/x-uplink-redact"},{"$ref":"#/components/parameters/x-uplink-cache"},{"$ref":"#/components/parameters/x-uplink-cache-ttl"},{"$ref":"#/components/parameters/x-uplink-cache-clear"},{"$ref":"#/components/parameters/x-uplink-max-tool-rounds"},{"$ref":"#/components/parameters/anthropic-version"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesRequest"}}}},"responses":{"200":{"description":"The model's answer in the Anthropic message shape, as its route returns it.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"application/json":{"schema":{"type":"object","description":"Anthropic message object.","additionalProperties":true}},"text/event-stream":{"schema":{"type":"string","description":"Anthropic message stream events."}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}}}}},"/v1/messages/count_tokens":{"post":{"operationId":"countMessageTokens","summary":"Count a message's input tokens, Anthropic format","description":"Anthropic's token count for a Messages request, before it is sent. Free, and counted by the gateway without calling the model: about four characters a token plus a few per message, with each image at about 1,600 tokens, so it is an estimate that errs high. The bill uses the count the model reports.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/anthropic-version"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesRequest"}}}},"responses":{"200":{"description":"The input tokens.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"}},"content":{"application/json":{"schema":{"type":"object","required":["input_tokens"],"properties":{"input_tokens":{"type":"integer"}}}}}},"400":{"description":"invalid_request: no model, or messages is not a list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1beta/models/{model}:generateContent":{"post":{"operationId":"generateContent","summary":"Generate content, Gemini format","description":"Google's generateContent request, passed through to the model named in the path. The Gemini SDK sends `x-goog-api-key`.","tags":["chat"],"parameters":[{"name":"model","in":"path","required":true,"description":"A model id from GET /v1/catalog that is served in the gemini wire format, or one of your aliases.","schema":{"type":"string"}},{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"},{"$ref":"#/components/parameters/x-session-id"},{"$ref":"#/components/parameters/x-customer-id"},{"$ref":"#/components/parameters/x-uplink-redact"},{"$ref":"#/components/parameters/x-uplink-cache"},{"$ref":"#/components/parameters/x-uplink-cache-ttl"},{"$ref":"#/components/parameters/x-uplink-cache-clear"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateContentRequest"}}}},"responses":{"200":{"description":"Gemini GenerateContentResponse, as the model's route returns it.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}}}}},"/v1beta/models/{model}:streamGenerateContent":{"post":{"operationId":"streamGenerateContent","summary":"Stream content, Gemini format","description":"Google's streamGenerateContent request. Send `?alt=sse` for server-sent events, as the Gemini SDK does.","tags":["chat"],"parameters":[{"name":"model","in":"path","required":true,"description":"A model id from GET /v1/catalog that is served in the gemini wire format, or one of your aliases.","schema":{"type":"string"}},{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"},{"$ref":"#/components/parameters/x-session-id"},{"$ref":"#/components/parameters/x-customer-id"},{"$ref":"#/components/parameters/x-uplink-redact"},{"$ref":"#/components/parameters/x-uplink-cache"},{"$ref":"#/components/parameters/x-uplink-cache-ttl"},{"$ref":"#/components/parameters/x-uplink-cache-clear"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateContentRequest"}}}},"responses":{"200":{"description":"A stream of Gemini GenerateContentResponse chunks.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-model":{"$ref":"#/components/headers/x-uplink-model"},"x-uplink-alias":{"$ref":"#/components/headers/x-uplink-alias"},"x-uplink-preset":{"$ref":"#/components/headers/x-uplink-preset"},"x-uplink-charge":{"$ref":"#/components/headers/x-uplink-charge"},"x-uplink-list-cost":{"$ref":"#/components/headers/x-uplink-list-cost"},"x-uplink-saved":{"$ref":"#/components/headers/x-uplink-saved"},"x-uplink-valid":{"$ref":"#/components/headers/x-uplink-valid"},"x-uplink-refund":{"$ref":"#/components/headers/x-uplink-refund"},"x-uplink-receipt":{"$ref":"#/components/headers/x-uplink-receipt"},"x-uplink-autopilot":{"$ref":"#/components/headers/x-uplink-autopilot"},"x-uplink-crowd-cache":{"$ref":"#/components/headers/x-uplink-crowd-cache"},"x-uplink-waste":{"$ref":"#/components/headers/x-uplink-waste"},"x-uplink-routing":{"$ref":"#/components/headers/x-uplink-routing"},"x-uplink-auto-tier":{"$ref":"#/components/headers/x-uplink-auto-tier"},"x-uplink-attempts":{"$ref":"#/components/headers/x-uplink-attempts"},"x-uplink-tool-calls":{"$ref":"#/components/headers/x-uplink-tool-calls"},"x-uplink-tool-charge":{"$ref":"#/components/headers/x-uplink-tool-charge"},"x-uplink-tool-rounds":{"$ref":"#/components/headers/x-uplink-tool-rounds"},"x-uplink-round-ids":{"$ref":"#/components/headers/x-uplink-round-ids"},"x-uplink-total-charge":{"$ref":"#/components/headers/x-uplink-total-charge"},"x-uplink-sandbox-session":{"$ref":"#/components/headers/x-uplink-sandbox-session"},"x-uplink-cache":{"$ref":"#/components/headers/x-uplink-cache"},"x-uplink-cache-ttl":{"$ref":"#/components/headers/x-uplink-cache-ttl"},"x-uplink-cache-age":{"$ref":"#/components/headers/x-uplink-cache-age"},"x-uplink-cache-source-id":{"$ref":"#/components/headers/x-uplink-cache-source-id"},"x-uplink-compressed":{"$ref":"#/components/headers/x-uplink-compressed"},"x-uplink-redactions":{"$ref":"#/components/headers/x-uplink-redactions"}},"content":{"text/event-stream":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}},"400":{"description":"invalid_request: not a JSON object, no model named, `models` not a list of up to 5 model ids, the model is served in another wire format, a bad `auto` field, or `auto` found no model this key may use for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"401":{"description":"invalid_api_key: no key, a key in the URL, or a key that is not valid or was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"402":{"description":"insufficient_balance: top up to continue. spend_cap_reached: a new account's cap before its first payment settles. monthly_limit_reached, weekly_limit_reached, daily_limit_reached: the key's spend limit for its period (a UTC month, an ISO week from Monday 00:00 UTC, or a UTC day). key_budget_exhausted: the key's own prepaid budget is used up; raise it or use another key. user_budget_reached: the request's end user (x-customer-id, or the `user` field) has spent the key's user_budget for its period; the same shape as the other 402s. With no credential header: the x402 quote (x402_payment_required, see the x402 note above), a payment not confirmed yet (x402_payment_pending), one that is spent (x402_payment_spent, with a fresh quote) or an unknown payment id (x402_payment_invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. admin_key: an admin key manages keys and cannot call models. credential_required: the model runs on your own provider key, which cannot be stored yet. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"404":{"description":"model_not_found: GET /v1/catalog lists every model id. preset_not_found: the request named a preset your organization does not have.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"413":{"description":"invalid_request: the body is over 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"424":{"description":"mcp_server_error: a declared remote MCP server could not be reached or listed (server tools). Nothing was run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute, its own concurrency_limit or the account's concurrency; retry-after says when. Defaults: 600 requests a minute per key, 50 requests in flight per account, 60 a minute for every key in an account's first 24 hours; higher on request. upstream_rate_limited: the model's route is busy; retry-after when the route gave one. session_cap: the session (x-session-id) reached its key's session_max_requests per hour or session_max_spend per UTC day; the error's session_cap says which, the cap and reset_at, and x-uplink-session-reset carries the same time. loop_detected: the key's loop guard saw this session send the same tool call (same name and arguments) or the same request body more than 5 times in 10 minutes; reset_at says when it may go again. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"502":{"description":"upstream_error: the model's route failed or its answer was cut off. Nothing reached you, so nothing was charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now. Nothing is charged. Carries retry-after: back off that long before trying again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}}}}},"/v1/models":{"get":{"operationId":"listModels","summary":"Models this key can call","description":"Every servable model the key's allowlist admits, then the organization's aliases, each naming its model in `uplink.alias_of`. The shape follows the credential header: OpenAI's list for a Bearer token, Anthropic's for `x-api-key`, Google's for `x-goog-api-key`.","tags":["models"],"responses":{"200":{"description":"The model list.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ModelListOpenAI"},{"$ref":"#/components/schemas/ModelListAnthropic"},{"$ref":"#/components/schemas/ModelListGemini"}]}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1beta/models":{"get":{"operationId":"listModelsGemini","summary":"Models this key can call, Gemini format","description":"The same list as GET /v1/models, always in Google's shape.","tags":["models"],"responses":{"200":{"description":"The model list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelListGemini"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/catalog":{"get":{"operationId":"getCatalog","summary":"Public catalog with prices","description":"Every servable chat model with the price you pay and the developer's list price, plus `server_tools`, `file_parsing`, `auto_router` and `x402` (paying per request without a key: null where it is off). No key. Any origin. Cached 5 minutes. This is where model ids are enumerated.","tags":["models"],"security":[],"responses":{"200":{"description":"The catalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"}}}}}}},"/v1/catalog/media":{"get":{"operationId":"getMediaCatalog","summary":"Public media catalog","description":"Every image, video, speech and 3D model: its settings, which routes take it, and the price per unit. No key. Any origin. Cached 5 minutes.","tags":["models","media"],"security":[],"responses":{"200":{"description":"The media catalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaCatalog"}}}}}}},"/v1/estimate":{"post":{"operationId":"estimateCost","summary":"Cost on every model before sending","description":"Public prices without a key; with a key, your organization's own prices and any price lock. Input tokens are estimated at 4 characters per token plus 4 per message unless `input_tokens` is given. Any origin.","tags":["pricing"],"security":[{},{"bearer":[]},{"apiKey":[]},{"googleApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstimateRequest"}}}},"responses":{"200":{"description":"The estimate, cheapest model first.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstimateResponse"}}}},"400":{"description":"invalid_request: not a JSON object, nothing to count, or `models` is not a list of up to the allowed number of ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key, when a key was sent and is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/requests/{request_id}":{"get":{"operationId":"getRequest","summary":"What a finished request cost","description":"One request from the usage log, for any key of the same organization: tokens, charge, the same tokens at the developer's list price, and the saving. A request lands in the log within about a minute of finishing. This is where a stream's figures are.","tags":["receipts"],"parameters":[{"$ref":"#/components/parameters/request_id"}],"responses":{"200":{"description":"The request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestRecord"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: not in the log yet, or not this organization's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/requests/{request_id}/receipt":{"get":{"operationId":"getSignedReceipt","summary":"The signed receipt for a request","description":"The receipt a non-streamed response carries in `x-uplink-receipt`, for any request including streams, as a document: the signed `payload` and the `receipt` it decodes to. Verify with the key at GET /v1/statements/public-key.","tags":["receipts"],"parameters":[{"$ref":"#/components/parameters/request_id"}],"responses":{"200":{"description":"The signed receipt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedReceipt"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: not in the log yet, or not this organization's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"not_configured: receipt signing is not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/images/generations":{"post":{"operationId":"createImage","summary":"Generate images, OpenAI image format","description":"OpenAI's image request for the image models. `size`, `quality` and `n` are mapped onto the model's own settings; anything else the model takes goes in `input`. The call waits up to about 100 seconds for the image. The price is quoted from the request before anything runs and charged when the model accepts the task; a failed generation is given back.","tags":["media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationRequest"}}}},"responses":{"200":{"description":"The images.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-generation-id":{"$ref":"#/components/headers/x-uplink-generation-id"},"x-uplink-charge-usd":{"$ref":"#/components/headers/x-uplink-charge-usd"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationResponse"}}}},"400":{"description":"invalid_request: the model makes something other than images, a setting is out of range, or the model refused these settings (upstream_invalid_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached or monthly_limit_reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog/media lists the media models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 1 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited, or upstream_rate_limited when the model is busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"generation_failed: the model could not complete it. Nothing is charged. upstream_error: the model failed to start it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: media generation is unavailable right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"generation_pending: still generating after the wait. The generation is charged and continues; fetch it at GET /v1/generations/{generation_id}.","headers":{"x-uplink-generation-id":{"$ref":"#/components/headers/x-uplink-generation-id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/images/edits":{"post":{"operationId":"editImage","summary":"Edit images, OpenAI image edit format","description":"OpenAI's image edit request on the models that take images (GET /v1/catalog/media lists `/v1/images/edits` among their endpoints). Name an edit model, or an image model whose `-edit` twin edits (gpt-image-2 goes to gpt-image-2-edit). Upload the images as multipart `image` or `image[]` files (PNG, JPEG or WebP, 20 MB each, 16 at most) with `prompt`, or send JSON with `images: [{ image_url }]`. `mask` is taken by the models that list `mask_url`. Uploads are kept beside the generated files for 30 days so the model can read them. Priced and charged as on POST /v1/images/generations; a failed edit is given back.","tags":["media"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["image","prompt","model"],"properties":{"image":{"type":"string","format":"binary","description":"The image to edit; repeat as image[] for several."},"mask":{"type":"string","format":"binary","description":"A mask whose transparent pixels mark the area to change, on models that take one."},"prompt":{"type":"string","description":"The edit to make."},"model":{"type":"string","description":"An edit model id, or an image model with an -edit twin."},"n":{"type":"integer","description":"Images to make, where the model makes more than one per request."},"size":{"type":"string","description":"WIDTHxHEIGHT or auto, mapped onto the model's own sizes."},"quality":{"type":"string","description":"low, medium or high, where the model has a quality setting."},"response_format":{"type":"string","enum":["url","b64_json"]}}}},"application/json":{"schema":{"type":"object","required":["model","prompt","images"],"properties":{"model":{"type":"string","description":"An edit model id, or an image model with an -edit twin."},"prompt":{"type":"string","description":"The edit to make."},"images":{"type":"array","items":{"type":"object","required":["image_url"],"properties":{"image_url":{"type":"string","description":"An https URL of the image."}}}},"mask":{"type":"object","properties":{"image_url":{"type":"string","description":"An https URL of the mask."}}},"response_format":{"type":"string","enum":["url","b64_json"]}}}}}},"responses":{"200":{"description":"The edited images.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-generation-id":{"$ref":"#/components/headers/x-uplink-generation-id"},"x-uplink-charge-usd":{"$ref":"#/components/headers/x-uplink-charge-usd"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationResponse"}}}},"400":{"description":"invalid_request: no image or prompt, a file that is not PNG, JPEG or WebP or is over 20 MB, a mask on a model that takes none, or a model that does not edit images. content_not_allowed: see the uncensored edit rules.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached or monthly_limit_reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 50 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited, or upstream_rate_limited when the model is busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"generation_failed: the model could not complete it. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"generation_pending: still editing after the wait. Fetch it at GET /v1/generations/{generation_id}.","headers":{"x-uplink-generation-id":{"$ref":"#/components/headers/x-uplink-generation-id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/search":{"post":{"operationId":"search","summary":"Web search","description":"One web search, the same results and price as the chat tools' `web_search` (GET /v1/catalog `server_tools`, unit search), billed on the key's balance as one search and refused with the model routes' 402 and 429 when the balance, the key's budget, its spend limit or its rate limit cannot cover it. `allowed_models` does not apply. A search the vendor cannot answer is not charged. The answer carries x-uplink-request-id, x-uplink-charge-usd and a signed x-uplink-receipt over its exact bytes.","tags":["search"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"The results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"invalid_request: no query, a query over 400 characters, max_results outside 1 to 10, or an unknown freshness.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached, monthly_limit_reached or key_budget_exhausted. With no credential header: the x402 quote or a refused payment (see the x402 note above).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"admin_key, ip_not_allowed or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: the key's rate limit. upstream_rate_limited: web search is busy; nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"search_failed: the search did not complete; nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"search_unavailable: web search is switched off.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/audio/transcriptions":{"post":{"operationId":"createTranscription","summary":"Transcribe audio, OpenAI audio format","description":"OpenAI's transcription request: the audio file and a transcription model from GET /v1/catalog (type transcription, priced per audio minute). Billed on the audio length the model reports, rounded up to the second. A failed request is not charged.","tags":["audio"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","model"],"properties":{"file":{"type":"string","format":"binary","description":"The audio: mp3, mp4, m4a, wav, webm, ogg, flac or aac, 25 MB at most."},"model":{"type":"string","description":"A transcription model id from GET /v1/catalog (type transcription)."},"language":{"type":"string","description":"ISO 639-1 code of the spoken language, for example en. Left out, the model detects it."},"prompt":{"type":"string","description":"Text that guides spelling and style (Whisper models)."},"response_format":{"type":"string","enum":["json","text","srt","verbose_json","vtt"],"description":"Default json."}}}}}},"responses":{"200":{"description":"The transcript: { text, usage } for json, plain text for text, subtitles for srt and vtt, and language, duration and segments as well for verbose_json. `usage.seconds` is the audio length billed.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-charge-usd":{"$ref":"#/components/headers/x-uplink-charge-usd"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcription"}},"text/plain":{"schema":{"type":"string"}},"text/vtt":{"schema":{"type":"string"}}}},"400":{"description":"invalid_request: not multipart, no file, an unknown response_format, or the model is not a transcription model (translations: a model that cannot translate). upstream_invalid_request: the model could not read the file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached, monthly_limit_reached or key_budget_exhausted. The hold covers the longest audio the file's size allows; the charge is the audio's length.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied, admin_key or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists the transcription models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the file is over 25 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited, or upstream_rate_limited when the model is busy. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model could not transcribe the file right now. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/audio/translations":{"post":{"operationId":"createTranslation","summary":"Translate audio into English, OpenAI audio format","description":"OpenAI's translation request: speech in any language transcribed into English text. Only models whose route can translate take it (whisper-large-v3-turbo). Billed per audio second like a transcription.","tags":["audio"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","model"],"properties":{"file":{"type":"string","format":"binary","description":"The audio: mp3, mp4, m4a, wav, webm, ogg, flac or aac, 25 MB at most."},"model":{"type":"string","description":"A transcription model id from GET /v1/catalog (type transcription)."},"language":{"type":"string","description":"ISO 639-1 code of the spoken language, for example en. Left out, the model detects it."},"prompt":{"type":"string","description":"Text that guides spelling and style (Whisper models)."},"response_format":{"type":"string","enum":["json","text","srt","verbose_json","vtt"],"description":"Default json."}}}}}},"responses":{"200":{"description":"The transcript: { text, usage } for json, plain text for text, subtitles for srt and vtt, and language, duration and segments as well for verbose_json. `usage.seconds` is the audio length billed.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-charge-usd":{"$ref":"#/components/headers/x-uplink-charge-usd"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcription"}},"text/plain":{"schema":{"type":"string"}},"text/vtt":{"schema":{"type":"string"}}}},"400":{"description":"invalid_request: not multipart, no file, an unknown response_format, or the model is not a transcription model (translations: a model that cannot translate). upstream_invalid_request: the model could not read the file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached, monthly_limit_reached or key_budget_exhausted. The hold covers the longest audio the file's size allows; the charge is the audio's length.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied, admin_key or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists the transcription models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the file is over 25 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited, or upstream_rate_limited when the model is busy. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model could not transcribe the file right now. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/audio/speech":{"post":{"operationId":"createSpeech","summary":"Text to speech, OpenAI speech format","description":"OpenAI's speech request for the text-to-speech models (GET /v1/catalog/media lists `/v1/audio/speech` among a model's endpoints). `input` is the text; `voice`, `response_format` and `speed` go to the model's own settings where it has them, and a model without a format choice answers in mp3. The answer is the audio itself. Priced and charged as on POST /v1/generations; a failed generation is given back.","tags":["media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","input"],"properties":{"model":{"type":"string","description":"A text-to-speech model id from GET /v1/catalog/media."},"input":{"type":"string","description":"The text to speak."},"voice":{"type":"string","description":"One of the model's voices (its `voice` parameter in the media catalog). Leave out for its default."},"response_format":{"type":"string","description":"mp3, or another format the model's `response_format` parameter lists."},"speed":{"type":"number","description":"Within the model's `speed` range, where it has one."}}}}}},"responses":{"200":{"description":"The audio.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"},"x-uplink-generation-id":{"$ref":"#/components/headers/x-uplink-generation-id"},"x-uplink-charge-usd":{"$ref":"#/components/headers/x-uplink-charge-usd"}},"content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}},"audio/wav":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"invalid_request: not a text-to-speech model, no input, or a voice, format or speed the model does not take.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached or monthly_limit_reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog/media lists the media models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"generation_failed: the model could not complete it. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"generation_pending: still generating after the wait. The generation is charged and continues; fetch it at GET /v1/generations/{generation_id}.","headers":{"x-uplink-generation-id":{"$ref":"#/components/headers/x-uplink-generation-id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generations":{"post":{"operationId":"createGeneration","summary":"Start a media generation","description":"Any media model, with the settings GET /v1/catalog/media lists for it under `params`. Answers 202 with the generation to poll. The price is charged when the model accepts the task; a failed generation is given back in full.","tags":["media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRequest"}}}},"responses":{"202":{"description":"The generation, queued.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"400":{"description":"invalid_request: a setting is missing, out of range or not one the model takes; or upstream_invalid_request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_balance, spend_cap_reached or monthly_limit_reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied or account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog/media lists the media models.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the body is over 1 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited, or upstream_rate_limited when the model is busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"upstream_error: the model failed to start this generation. Nothing is charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: media generation is unavailable right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generations/{generation_id}":{"get":{"operationId":"getGeneration","summary":"A generation's status and files","description":"Reads the model's progress while the generation is open (at most once every 2 seconds) and, once it succeeded, lists its files. Files are kept 30 days.","tags":["media"],"parameters":[{"$ref":"#/components/parameters/generation_id"}],"responses":{"200":{"description":"The generation.","headers":{"x-uplink-request-id":{"$ref":"#/components/headers/x-uplink-request-id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such generation in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/files/{path}":{"get":{"operationId":"getFile","summary":"A generated file","description":"A file from a finished generation, at the URL its `outputs` give: `{date}/{generation_id}/{n}.{ext}`. No key: the path is the capability. Served with `cache-control: public, max-age=604800, immutable` from any origin for 30 days after the generation. HEAD is served too.","tags":["media"],"security":[],"parameters":[{"$ref":"#/components/parameters/file_path"}],"responses":{"200":{"description":"The file.","headers":{"etag":{"description":"The stored object's entity tag.","schema":{"type":"string"}}},"content":{"image/*":{"schema":{"type":"string","format":"binary"}},"video/*":{"schema":{"type":"string","format":"binary"}},"audio/*":{"schema":{"type":"string","format":"binary"}},"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"not_found: no such file, or it is past 30 days.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/files":{"post":{"operationId":"createFile","summary":"Upload a batch input file","description":"Multipart `file` (JSONL, 50 MB and 50,000 lines at most) with `purpose` batch. Each line: `{ custom_id, method: \"POST\", url, body }`, url one of /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/responses, /v1/moderations, custom_id unique. Every line is checked on upload; the first bad one is named. Kept 30 days.","tags":["batch"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","purpose"],"properties":{"file":{"type":"string","format":"binary"},"purpose":{"type":"string","enum":["batch"]}}}}}},"responses":{"200":{"description":"The file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchFile"}}}},"400":{"description":"invalid_request: purpose is not batch, no file, or a line that is not a valid batch request (named by number).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"invalid_request: the file is over 50 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listFiles","summary":"List the organization's files","description":"Batch input files and the output and error files batches wrote, newest first. `purpose` filters (batch, batch_output); `limit` up to 10,000, default 100.","tags":["batch"],"responses":{"200":{"description":"The files.","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/BatchFile"}},"has_more":{"type":"boolean"}}}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/files/{file_id}":{"get":{"operationId":"retrieveFile","summary":"A batch file","tags":["batch"],"parameters":[{"$ref":"#/components/parameters/file_id"}],"responses":{"200":{"description":"The file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchFile"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such file in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteFile","summary":"Delete a batch file","tags":["batch"],"parameters":[{"$ref":"#/components/parameters/file_id"}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","required":["id","object","deleted"],"properties":{"id":{"type":"string"},"object":{"const":"file"},"deleted":{"const":true}}}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such file in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/files/{file_id}/content":{"get":{"operationId":"downloadFile","summary":"A batch file's content","description":"The JSONL as uploaded, or a batch's output or error file: one line per request, `{ id, custom_id, response: { status_code, request_id, body }, error }`.","tags":["batch"],"parameters":[{"$ref":"#/components/parameters/file_id"}],"responses":{"200":{"description":"JSONL.","content":{"application/jsonl":{"schema":{"type":"string"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such file in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/batches":{"post":{"operationId":"createBatch","summary":"Create a batch","description":"Runs every line of an uploaded file within 24 hours, through the same routes as a direct request with the key that creates the batch: priced at the catalog rates less the batch discount of 50% on every line (the receipt and the usage log show the discounted charge), held against the balance, the key's budget and limits as each line runs. Lines run a few at a time per organization. Answers 2xx go to the output file, the rest to the error file; lines not run by the end of the window go to the error file with code batch_expired.","tags":["batch"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["input_file_id","endpoint","completion_window"],"properties":{"input_file_id":{"type":"string","description":"A file uploaded with purpose batch."},"endpoint":{"type":"string","enum":["/v1/chat/completions","/v1/completions","/v1/embeddings","/v1/responses","/v1/moderations"],"description":"Every line's url must match."},"completion_window":{"type":"string","enum":["24h"]},"metadata":{"type":"object","description":"Up to 16 string values, returned with the batch.","additionalProperties":{"type":"string"}}}}}}},"responses":{"200":{"description":"The batch, in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"400":{"description":"invalid_request: an unknown endpoint, a completion_window other than 24h, or a line whose url is not the batch's endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such input file in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listBatches","summary":"List the organization's batches","description":"Newest first. `limit` up to 100 (default 20); `after` a batch id to page on from.","tags":["batch"],"responses":{"200":{"description":"The batches.","content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}},"first_id":{"type":["string","null"]},"last_id":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/batches/{batch_id}":{"get":{"operationId":"retrieveBatch","summary":"A batch and its request counts","tags":["batch"],"parameters":[{"$ref":"#/components/parameters/batch_id"}],"responses":{"200":{"description":"The batch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such batch in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/batches/{batch_id}/cancel":{"post":{"operationId":"cancelBatch","summary":"Cancel a batch","description":"Lines not started are left out; lines already answered stay in the output file. The batch is cancelling until its running lines finish, then cancelled.","tags":["batch"],"parameters":[{"$ref":"#/components/parameters/batch_id"}],"responses":{"200":{"description":"The batch, cancelling or cancelled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such batch in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"batch_not_cancellable: the batch already finished.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/keys":{"get":{"operationId":"listKeys","summary":"List the organization's keys","description":"The key API. Needs an admin key, created once in the console (Keys, \"Admin key\"), sent as `Authorization: Bearer sk-relay-live-...`. An admin key manages keys and cannot call models. Active keys unless `include_revoked=true`. Never contains a secret.","tags":["keys"],"security":[{"adminKey":[]}],"parameters":[{"name":"include_revoked","in":"query","description":"`true` lists paused and revoked keys too.","schema":{"type":"string","enum":["true"]}}],"responses":{"200":{"description":"The keys, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyList"}}}},"401":{"description":"invalid_api_key: the key does not authenticate.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: a key that is not an admin key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createKey","summary":"Create a key","description":"Mints a key for your organization and returns the full secret once; afterwards only its prefix is shown. A `budget_usd` is a hard cap for that key alone, carved out of your prepaid balance: requests on it are refused with 402 key_budget_exhausted once its charges plus open holds reach the budget, while your balance is still what is debited. The console's field names (rate_limit_rpm, tpm_limit, monthly_spend_limit_micro, budget_micro) are accepted too. An admin key cannot create another admin key.","tags":["keys"],"security":[{"adminKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyCreate"}}}},"responses":{"201":{"description":"The key, with its secret in `key`. This is the only time it is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyCreated"}}}},"400":{"description":"invalid_request: a field out of range, or not a JSON object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: an admin key asked for scope admin. key_unavailable: new keys are off for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"limit_reached: 50 active keys already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/keys/{key_id}":{"get":{"operationId":"getKey","summary":"One key","tags":["keys"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/key_id"}],"responses":{"200":{"description":"The key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyEnvelope"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no such key in your organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateKey","summary":"Change a key","description":"Any subset of the fields. `budget_usd` may go up or down but never under what the key has already spent against it (400 budget_below_spent); null removes the budget. `status: \"revoked\"` pauses a key and `\"active\"` resumes it.","tags":["keys"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/key_id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyPatch"}}}},"responses":{"200":{"description":"The key as it now is.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyEnvelope"}}}},"400":{"description":"invalid_request, or budget_below_spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"key_deleted: a revoked key cannot be changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"revokeKey","summary":"Revoke a key","description":"Revokes the key for good: its next request is refused at once. Its usage and receipts stay readable.","tags":["keys"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/key_id"}],"responses":{"200":{"description":"The revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyEnvelope"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/keys/{key_id}/usage":{"get":{"operationId":"getKeyUsage","summary":"A key's usage","description":"Requests and spend on the key for the current UTC day, the current UTC month and its lifetime, from the usage log, with the budget and what is left of it when the key has one. `?group_by=user` adds `by_user`: this month's requests and spend per end user (x-customer-id, else the `user` field), top 100 by spend.","tags":["keys"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/key_id"},{"name":"group_by","in":"query","description":"`user` adds the per-end-user breakdown for the current UTC month.","schema":{"type":"string","enum":["user"]}}],"responses":{"200":{"description":"The usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyUsageEnvelope"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/sessions":{"get":{"operationId":"listSessions","summary":"Recent sessions","description":"Requests grouped by session, newest activity first. A request joins a session through the `x-session-id` header (or `x-uplink-session`, or a `session_id` or `user` field in the body); a hosted agent's turns use `<agent id>:<session>`. Each row counts requests, models, keys, tokens, cost and failed requests between the first request's start and the last one's end.","tags":["sessions"],"security":[{"adminKey":[]}],"parameters":[{"name":"key_id","in":"query","description":"Only this key's requests.","schema":{"type":"string"}},{"name":"days","in":"query","description":"How far back, 1 to 90. 30 unless set.","schema":{"type":"integer","minimum":1,"maximum":90}},{"name":"limit","in":"query","description":"1 to 200. 50 unless set.","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"before","in":"query","description":"`next_before` of the previous page.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The sessions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionList"}}}},"401":{"description":"unauthorized: no admin key and no console sign-in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/sessions/{session_id}":{"get":{"operationId":"getSession","summary":"One session's timeline","description":"Every request of the session in the order they started (up to 1,000), with model, tokens, cost, latency and outcome, plus the totals.","tags":["sessions"],"security":[{"adminKey":[]}],"parameters":[{"name":"session_id","in":"path","required":true,"description":"The session id, URL-encoded.","schema":{"type":"string","pattern":"^[\\x20-\\x7E]{1,128}$"}}],"responses":{"200":{"description":"The timeline.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionEnvelope"}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: no request of your organization carried this session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/usage/export":{"get":{"operationId":"exportUsageCsv","summary":"Usage log as CSV","description":"The organization's requests from `from` to `to`, oldest first, as CSV: request_id, created_at, key_id, session_id, model, outcome, status, input_tokens, cache_read_tokens, cache_write_tokens, output_tokens, charge_usd, latency_ms, refund_reason, user. At most 92 days and 200,000 rows per file.","tags":["sessions"],"security":[{"adminKey":[]}],"parameters":[{"name":"from","in":"query","description":"YYYY-MM-DD (UTC, inclusive) or Unix milliseconds. 30 days before `to` unless set.","schema":{"type":"string"}},{"name":"to","in":"query","description":"YYYY-MM-DD (UTC, the whole day included) or Unix milliseconds. Now unless set.","schema":{"type":"string"}},{"name":"key_id","in":"query","description":"Only this key's requests.","schema":{"type":"string"}},{"name":"model","in":"query","description":"Only requests for this model id.","schema":{"type":"string"}},{"name":"status","in":"query","description":"Only this outcome, or `failed` for upstream_error and upstream_rejected together.","schema":{"type":"string","enum":["completed","client_disconnected","upstream_error","upstream_rejected","cache_hit","failed"]}},{"name":"user","in":"query","description":"Only this end user's requests (x-customer-id, else the `user` field).","schema":{"type":"string"}}],"responses":{"200":{"description":"The CSV file.","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"invalid_request: from is not before to, or the range is over 92 days.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/exports":{"get":{"operationId":"listExports","summary":"Log export destinations","description":"Where each finished request is sent. Never shows a secret: a destination's `summary` names its host, the first characters of a Langfuse public key, or the header names it sends.","tags":["exports"],"security":[{"adminKey":[]}],"responses":{"200":{"description":"The destinations.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExportDestination"}}}}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createExport","summary":"Add a log export destination","description":"Batches of finished requests are sent within about a minute of each request, retried 3 times (10 s, 60 s, 5 min) on a network error, 408, 429 or 5xx. After 50 failed deliveries in a row the destination is switched off and the organization's address gets one email. `webhook`: POST JSON `{ event: \"usage.batch\", destination_id, created_at, data: [event...] }` signed with `x-uplink-timestamp` and `x-uplink-signature: sha256=<hex HMAC-SHA256 of \"<timestamp>.<body>\">` under the `webhook_secret` returned once here. `langfuse`: each request as a generation through Langfuse's OpenTelemetry endpoint, grouped by session. `otlp`: OTLP/HTTP JSON to `<endpoint>/v1/traces` with your headers (Datadog, Honeycomb, Grafana and others); a session is one trace and each request a span with `gen_ai.*` attributes. Events never name the infrastructure that served a request. A request body is included only when `include_bodies` is on and the request's key keeps bodies (`retain_bodies`).","tags":["exports"],"security":[{"adminKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportCreate"}}}},"responses":{"201":{"description":"The destination; a webhook's `webhook_secret` is returned only here.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ExportDestination"},"webhook_secret":{"type":"string","description":"Webhook only. Store it now; it is not returned again."}}}}}},"400":{"description":"invalid_request: an unknown kind, a URL that is not public https, a missing key, or a key id from another organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: only an owner (or an admin key) may change exports.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"limit_reached: 10 destinations already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/exports/{destination_id}":{"get":{"operationId":"getExport","summary":"One destination and its last 50 deliveries","tags":["exports"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/destination_id"}],"responses":{"200":{"description":"The destination.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ExportDestinationDetail"}}}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateExport","summary":"Change a destination","description":"Any subset of name, enabled, key_ids, include_bodies and the kind's own config fields. Switching a destination back on restarts its failure count.","tags":["exports"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/destination_id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportPatch"}}}},"responses":{"200":{"description":"The destination as it now is.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ExportDestination"}}}}}},"400":{"description":"invalid_request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteExport","summary":"Remove a destination","description":"Nothing more is sent to it, including batches already queued.","tags":["exports"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/destination_id"}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"string"}}}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/presets":{"get":{"operationId":"listPresets","summary":"List the organization's presets","description":"Every preset with its configuration, the 50 latest changes across them, and the limits. An admin key, or a console session.","tags":["presets"],"security":[{"adminKey":[]}],"responses":{"200":{"description":"The presets, by name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresetList"}}}},"401":{"description":"unauthorized: no admin key and no console session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: a key that is not an admin key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createPreset","summary":"Create a preset","description":"Creates a preset under `name`. 409 preset_exists when the name is taken; PUT /v1/presets/{name} changes one. At most 100 presets per organization.","tags":["presets"],"security":[{"adminKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PresetInput"},{"type":"object","required":["name"],"properties":{"name":{"$ref":"#/components/schemas/PresetName"}}}]}}}},"responses":{"201":{"description":"The preset, at version 1.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresetEnvelope"}}}},"400":{"description":"invalid_request: a field out of range: a bad name, a param that is not a preset param, a system prompt over 32 KB, more than 5 fallbacks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found: the model or a fallback is neither a catalog model nor one of your aliases.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"preset_exists, or limit_reached: 100 presets already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/presets/{name}":{"get":{"operationId":"getPreset","summary":"One preset","description":"The preset and its version history, newest first; every version keeps its whole configuration.","tags":["presets"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/preset_name"}],"responses":{"200":{"description":"The preset and its history.","content":{"application/json":{"schema":{"type":"object","required":["data","history"],"properties":{"data":{"$ref":"#/components/schemas/Preset"},"history":{"type":"array","items":{"$ref":"#/components/schemas/PresetChange"}}}}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"preset_not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"putPreset","summary":"Create or replace a preset","description":"Saves the whole configuration as a new version; the previous one stays in the history. An unchanged body makes no new version.","tags":["presets"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/preset_name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresetInput"}}}},"responses":{"200":{"description":"The preset as it now is.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresetEnvelope"}}}},"201":{"description":"A new preset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresetEnvelope"}}}},"400":{"description":"invalid_request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"limit_reached: 100 presets already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deletePreset","summary":"Delete a preset","description":"Requests naming it are refused with 404 preset_not_found from then on. The deletion is recorded in the history.","tags":["presets"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/preset_name"}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"string"}}}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"preset_not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/status":{"get":{"operationId":"getStatus","summary":"Service status, last 30 days, incidents of the last 90","description":"One entry per public service from the hourly probes, with a day-by-day count, the incidents of the last 90 days (opened and resolved by the same hourly check, with staff post-mortems) and the daily failover rehearsal per wire format. No key. Any origin. Cached 60 seconds.","tags":["status"],"security":[],"responses":{"200":{"description":"The status feed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusFeed"}}}}}}},"/v1/status/subscribe":{"post":{"operationId":"subscribeStatus","summary":"Status emails for an address","description":"Sends a confirmation email (double opt-in) for this API host's brand; once the link in it is opened, the address gets one email when an incident opens and one when it resolves, each with an unsubscribe link (GET /v1/status/unsubscribe?token=). An address already pending gets no second email for a day. No key. Any origin.","tags":["status"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"pending: a confirmation email went out (or is still valid). subscribed: the address was already confirmed.","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["pending","subscribed"]}}}}}},"400":{"description":"invalid_request: email is not a valid address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/proof":{"get":{"operationId":"getProof","summary":"Daily real-model check","description":"Each day a fixed prompt goes to every route of every model. This feed says, per model, whether its fingerprint held since the last check. No key. Any origin. Cached 5 minutes.","tags":["status"],"security":[],"responses":{"200":{"description":"The verdicts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Proof"}}}}}}},"/v1/stats/apps":{"get":{"operationId":"getApps","summary":"Usage by app, last 7 and 30 days","description":"Requests served and tokens per app over the last 7 and 30 full UTC days. Apps on a fixed list of public tools are named, matched from the X-Title header, else the HTTP-Referer host, else the User-Agent's product name; every other app is summed as Other. No key. Any origin. Cached 10 minutes.","tags":["status"],"security":[],"responses":{"200":{"description":"The usage by app.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppUsage"}}}}}}},"/v1/statements/public-key":{"get":{"operationId":"getPublicKey","summary":"Public key for statements and receipts","description":"The Ed25519 key that signs monthly statements and model receipts, as a JWK. No key. Any origin. Cached 1 hour.","tags":["receipts"],"security":[],"responses":{"200":{"description":"The public key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicKey"}}}},"503":{"description":"not_configured: statement signing is not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleError"}}}}}}},"/v1/guardrails":{"get":{"operationId":"getGuardrails","summary":"The organization's default guardrails","description":"The guardrails every key of the organization layers on; a key's own (the key's `guardrails` field) combine with them stricter-wins: blocked patterns are the union, the stricter prompt_injection applies, deny lists are the union and allow lists intersect. `data` is null when none are set. Guardrails are off unless you set them.","tags":["guardrails"],"security":[{"adminKey":[]}],"responses":{"200":{"description":"The default, or null.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuardrailsEnvelope"}}}},"401":{"description":"invalid_api_key, or no console session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"setGuardrails","summary":"Set the organization's default guardrails","description":"Replaces the default: `{ guardrails: {...} }`, or `{ guardrails: null }` to clear it. Keys pick the change up within a minute. A request a guardrail blocks answers 400 guardrail_blocked, reaches no model and costs nothing; a flag-mode match answers as usual with `x-uplink-guardrail: flagged`.","tags":["guardrails"],"security":[{"adminKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["guardrails"],"properties":{"guardrails":{"oneOf":[{"$ref":"#/components/schemas/Guardrails"},{"type":"null"}]}}}}}},"responses":{"200":{"description":"The default as it now is.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuardrailsEnvelope"}}}},"400":{"description":"invalid_request, or invalid_regex_pattern: a pattern that does not compile or could backtrack without bound (backreferences, lookaround, a repeated group holding a repeat or alternatives).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: only an owner (or an admin key) changes it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/auth/keys":{"post":{"operationId":"exchangeAuthCode","summary":"Sign in with RelaySwitch: trade a code for a key","description":"The last step of app sign-in (OAuth 2.0 with PKCE, S256). Send your user to `https://app.relayswitch.io/auth?callback_url=...&code_challenge=...&code_challenge_method=S256` (optional `key_label`, `limit` in USD, `state`); after they approve, the browser lands on callback_url with `?code=`. POST the code and your code_verifier here for a new API key owned by the user's organization, named key_label, with `limit` as its prepaid budget. The callback must be https, or http on localhost, 127.0.0.1 or [::1]. Codes are single use and expire 10 minutes after approval; the first attempt spends a code, matched or not. No key needed. Any origin.","tags":["sign-in"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthKeyExchange"}}}},"responses":{"200":{"description":"The new key. Store it now; it is not returned again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthKeyExchanged"}}}},"400":{"description":"invalid_grant: the code is invalid, expired or already used, the verifier does not match, or callback_url is not the approved one. invalid_request, invalid_code_challenge_method.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"key_unavailable: new keys are off for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"limit_reached: the organization already holds 50 active keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/webhooks":{"get":{"operationId":"listWebhooks","summary":"The organization's account-event webhooks","description":"Every registered endpoint receives every account event: balance.low and balance.empty (with the balance notices), key.budget_reached and key.limit_reached (a key at 100% of its budget or spend limit), topup.credited (a payment credited), payment.failed (a saved-card charge declined), incident.opened and incident.resolved (a public status service degraded or down, and back; the data is the incident as GET /v1/status lists it). Each POST carries x-uplink-timestamp and x-uplink-signature: sha256=<hex HMAC-SHA256 of \"<timestamp>.<body>\"> under the endpoint's secret. Three attempts (at once, after 2 s, after 8 s) until a 2xx.","tags":["webhooks"],"security":[{"adminKey":[]}],"responses":{"200":{"description":"The endpoints. Secrets are never returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookList"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createWebhook","summary":"Register a webhook URL","description":"A public https URL. The answer carries `secret` once; store it to check signatures. At most three endpoints per organization.","tags":["webhooks"],"security":[{"adminKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreate"}}}},"responses":{"201":{"description":"The endpoint, with its secret this once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreated"}}}},"400":{"description":"invalid_request: not a public https URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: only an owner (or an admin key) registers one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"limit_reached: three endpoints already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/webhooks/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"The last 50 webhook deliveries","description":"Newest first: endpoint, event, attempts, status (pending, delivered, failed) and the last HTTP status.","tags":["webhooks"],"security":[{"adminKey":[]}],"responses":{"200":{"description":"The deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryList"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/webhooks/{webhook_id}":{"delete":{"operationId":"deleteWebhook","summary":"Remove a webhook","tags":["webhooks"],"security":[{"adminKey":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"string","description":"The webhook id."}}}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/invoices":{"get":{"operationId":"listInvoices","summary":"The organization's invoices","description":"Every credited top-up (card and crypto), newest first, with its number, amount, method and PDF path. Numbers are INV-<year>-<n>, counted per organization and UTC year.","tags":["invoices"],"security":[{"adminKey":[]}],"responses":{"200":{"description":"The invoices.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}}}}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/invoices/{invoice_number}.pdf":{"get":{"operationId":"getInvoicePdf","summary":"One invoice as a PDF","description":"Rendered on request from the organization's current billing details (GET /v1/account/billing-details): the seller, the customer, the amount paid, the payment method as the console names it, the date and the balance credited. Served as `application/pdf` with a `content-disposition` attachment.","tags":["invoices"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/invoice_number"}],"responses":{"200":{"description":"The PDF.","content":{"application/pdf":{"schema":{"type":"string"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: not this organization's invoice.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/account/billing-details":{"get":{"operationId":"getBillingDetails","summary":"The organization's billing details","description":"What the invoices print under 'Bill to': company, address lines, country, tax id and email. Console sign-in only.","tags":["invoices"],"responses":{"200":{"description":"The details, every field null or empty until saved.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingDetails"}}}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"setBillingDetails","summary":"Save billing details","description":"Owner or billing role. A field left out keeps its value; null or \"\" clears it. At most 4 address lines of 120 characters.","tags":["invoices"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingDetails"}}}},"responses":{"200":{"description":"The details as saved.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingDetails"}}}}}},"400":{"description":"invalid_request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: a member without the billing role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/mcp-servers":{"get":{"operationId":"listMcpServers","summary":"The organization's MCP servers","description":"The servers /mcp/org aggregates. Header values are never returned; `header_names` says which are stored.","tags":["mcp"],"security":[{"adminKey":[]}],"responses":{"200":{"description":"The servers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServerList"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createMcpServer","summary":"Register an MCP server","description":"A Streamable HTTP MCP server at a public https URL (no private, loopback, link-local or reserved address, checked again before every call; redirects are not followed), with headers sent on every call (sealed at rest) and an optional tool allowlist. Its tools appear on /mcp/org as `name__tool`. Up to 20 per organization.","tags":["mcp"],"security":[{"adminKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServerCreate"}}}},"responses":{"201":{"description":"The server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServerEnvelope"}}}},"400":{"description":"invalid_url: not a public https URL. invalid_request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"name_taken, or limit_reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/mcp-servers/calls":{"get":{"operationId":"listMcpCalls","summary":"Recent /mcp/org tool calls","description":"Newest first: time, server, tool, status (ok, tool_error, failed, denied by the server's allowlist, blocked by the key's tool policy), latency. Arguments only for calls made with a key that keeps bodies. `limit` up to 200, `server_id` to filter.","tags":["mcp"],"security":[{"adminKey":[]}],"parameters":[{"name":"limit","in":"query","description":"1 to 200; 50 unless set.","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"server_id","in":"query","description":"One server's calls.","schema":{"type":"string"}}],"responses":{"200":{"description":"The calls.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpCallList"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/mcp-servers/{server_id}":{"get":{"operationId":"getMcpServer","summary":"One MCP server","tags":["mcp"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/server_id"}],"responses":{"200":{"description":"The server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServerEnvelope"}}}},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateMcpServer","summary":"Change an MCP server","description":"Any subset of the fields. `headers` replaces every stored header; null clears them.","tags":["mcp"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/server_id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServerPatch"}}}},"responses":{"200":{"description":"The server as it now is.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServerEnvelope"}}}},"400":{"description":"invalid_url, invalid_request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteMcpServer","summary":"Remove an MCP server","tags":["mcp"],"security":[{"adminKey":[]}],"parameters":[{"$ref":"#/components/parameters/server_id"}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"string"}}}}}},"404":{"description":"not_found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/mcp/org":{"post":{"operationId":"orgMcp","summary":"Organization MCP gateway (Streamable HTTP)","description":"One MCP endpoint for every server registered under /v1/mcp-servers: tools/list returns their tools named `server__tool` (each server's allowlist and the key's tool_policy applied), tools/call goes to the right server with its stored headers and is logged. Needs an API key (not an admin key). Stateless JSON-RPC over POST.","tags":["mcp"],"security":[{"bearer":[]},{"apiKey":[]}],"requestBody":{"required":true,"description":"A JSON-RPC 2.0 request, notification or batch.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"The JSON-RPC response.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"202":{"description":"A notification was accepted."},"401":{"description":"invalid_api_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"admin_key: use an API key. app_key: a key issued to an app through sign-in is for model requests only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/mcp":{"post":{"operationId":"mcp","summary":"MCP server (Streamable HTTP)","description":"The Model Context Protocol endpoint: JSON-RPC over POST, stateless. Tools: list_models, list_media_models, estimate and proof (no key) and chat (billed to the Authorization header). The server card is at GET /mcp/server-card and the catalog that points at it at /.well-known/ai-catalog.json. Any origin.","tags":["meta"],"security":[],"requestBody":{"required":true,"description":"A JSON-RPC 2.0 request, notification or batch.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"The JSON-RPC response.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"202":{"description":"A notification was accepted."},"400":{"description":"Not a valid MCP request.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/mcp/server-card":{"get":{"operationId":"getMcpServerCard","summary":"MCP server card","description":"The static card that says where the MCP server is and how to connect (SEP-2127). application/mcp-server-card+json. No key. Any origin. Cached 1 hour.","tags":["meta"],"security":[],"responses":{"200":{"description":"The server card.","content":{"application/mcp-server-card+json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This document","description":"Also at /v1/openapi.json. No key. Any origin. Cached 1 hour.","tags":["meta"],"security":[],"responses":{"200":{"description":"The OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer sk-relay-live-...`. The OpenAI SDK sends this."},"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"The same key in `x-api-key`, as the Anthropic SDK sends it. On GET /v1/models it selects Anthropic's list shape."},"googleApiKey":{"type":"apiKey","in":"header","name":"x-goog-api-key","description":"The same key in `x-goog-api-key`, as the Gemini SDK sends it. On GET /v1/models it selects Google's list shape."},"adminKey":{"type":"http","scheme":"bearer","description":"An admin key, created in the console: `Authorization: Bearer sk-relay-live-...`. It manages keys (/v1/keys) and log exports (/v1/exports), reads sessions and the usage CSV, and cannot call models."}},"schemas":{"ModelId":{"type":"string","description":"A model id from GET /v1/catalog, one of your organization's aliases, `@preset/<name>` for one of its presets, or `auto` (also `uplink/auto` or `<brand>/auto`) to let the auto router pick. Not enumerated here."},"FallbackModels":{"type":"array","maxItems":5,"items":{"type":"string"},"description":"Up to 5 model ids or aliases tried in order after `model` once every route of the one before has failed before a byte was sent. Each is billed at its own price when it answers; the key's allowlist applies to each."},"PresetName":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,63}$","description":"Lower-case letters, digits, dots, dashes and underscores; called as `@preset/<name>`. `preset` is reserved."},"PresetInput":{"type":"object","required":["model"],"properties":{"model":{"type":"string","description":"A model id from GET /v1/catalog, or one of your aliases."},"models":{"$ref":"#/components/schemas/FallbackModels"},"params":{"type":"object","description":"Defaults the request's own fields override: temperature, top_p, top_k, min_p, frequency_penalty, presence_penalty, repetition_penalty, max_tokens, max_completion_tokens, max_output_tokens, seed, stop, stop_sequences, reasoning, reasoning_effort, thinking, response_format, verbosity. Each is named for the request's format (max_tokens becomes max_output_tokens on /v1/responses; stop becomes stop_sequences on /v1/messages); a request that sets any output limit, reasoning setting or stop keeps its own.","additionalProperties":true},"system_prompt":{"type":["string","null"],"description":"Up to 32 KB. On /v1/chat/completions a leading system message, on /v1/messages `system`, on /v1/responses `instructions`."},"system_mode":{"type":"string","enum":["prepend","replace"],"description":"prepend (default): ahead of the request's own system prompt. replace: in its place."},"tools":{"type":["array","null"],"maxItems":128,"items":{"type":"object","additionalProperties":true},"description":"Tool declarations in the request's format, server tools included. Merged with the request's: a request tool replaces the preset tool of the same name (function name, or type for a server tool); the rest are appended."},"description":{"type":["string","null"],"description":"Up to 280 characters."}}},"Preset":{"type":"object","required":["name","version","model","models","params","system_prompt","system_mode","tools","description","created_at","updated_at"],"properties":{"name":{"$ref":"#/components/schemas/PresetName"},"version":{"type":"integer","description":"1 on creation, one more on each change."},"model":{"type":"string"},"models":{"type":"array","items":{"type":"string"}},"params":{"type":"object","additionalProperties":true},"system_prompt":{"type":["string","null"]},"system_mode":{"type":"string","enum":["prepend","replace"]},"tools":{"type":["array","null"],"items":{"type":"object","additionalProperties":true}},"description":{"type":["string","null"]},"created_at":{"type":"integer","description":"Unix milliseconds."},"updated_at":{"type":"integer","description":"Unix milliseconds."}}},"PresetChange":{"type":"object","required":["name","version","config","changed_at"],"properties":{"name":{"type":"string"},"version":{"type":"integer"},"config":{"anyOf":[{"$ref":"#/components/schemas/PresetInput"},{"type":"null"}],"description":"The whole configuration of this version; null for a deletion."},"changed_at":{"type":"integer","description":"Unix milliseconds."},"changed_by":{"type":["string","null"],"description":"The console user, or key:<id> for an admin key."}}},"PresetEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Preset"}}},"PresetList":{"type":"object","required":["data","history","limits"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Preset"}},"history":{"type":"array","items":{"$ref":"#/components/schemas/PresetChange"}},"limits":{"type":"object","properties":{"presets":{"type":"integer","description":"Presets per organization."},"system_prompt_bytes":{"type":"integer","description":"Largest system prompt."},"fallback_models":{"type":"integer","description":"Most fallback models."}}}}},"Key":{"type":"object","description":"A key as the key API and the console show it. Never the secret.","required":["id","name","key_prefix","environment","scope","status","rate_limit_rpm","tpm_limit","monthly_spend_limit_micro","budget_micro","budget_spent_micro","budget_remaining_micro","allowed_models","created_at","expires_at","month_spend_micro"],"properties":{"id":{"type":"string","description":"The key id: the twelve characters after the environment in the key itself."},"name":{"type":"string"},"key_prefix":{"type":"string","description":"The part that is safe to show, for example sk-relay-live-k7m2."},"environment":{"type":"string","enum":["live","test"]},"scope":{"type":"string","enum":["api","admin"],"description":"api calls models; admin manages keys."},"status":{"type":"string","enum":["active","revoked"]},"rate_limit_rpm":{"type":["integer","null"],"description":"Requests per minute, or null for the default."},"tpm_limit":{"type":["integer","null"],"description":"Tokens per minute, or null for none."},"monthly_spend_limit_micro":{"type":["integer","null"],"description":"Micro-dollars the key may be charged per spend_limit_period (a UTC month unless set), or null for none."},"spend_limit_period":{"type":"string","enum":["daily","weekly","monthly"],"description":"When the spend limit resets: 00:00 UTC each day, Monday 00:00 UTC each week, or the first of each UTC month (the default)."},"period_spend_micro":{"type":"integer","description":"Micro-dollars charged to the key so far in its current spend_limit_period."},"concurrency_limit":{"type":["integer","null"],"description":"Requests this key may have in flight at once, or null for the account's default only."},"month_spend_micro":{"type":"integer","description":"Micro-dollars charged to the key so far this UTC month."},"budget_micro":{"type":["integer","null"],"description":"The key's prepaid budget in micro-dollars (1,000,000 = $1), or null for none."},"budget_spent_micro":{"type":"integer","description":"Micro-dollars settled against the budget since it was set."},"budget_remaining_micro":{"type":["integer","null"],"description":"budget_micro minus budget_spent_micro, never under 0; null without a budget."},"allowed_models":{"type":["array","null"],"items":{"type":"string"},"description":"The model ids the key may call, or null for every model."},"allowed_ips":{"type":["array","null"],"items":{"type":"string"},"description":"IPv4/IPv6 addresses or CIDRs the key may be used from, or null for anywhere. A request from another address answers 403 ip_not_allowed."},"routing":{"type":"string","enum":["cheapest","fastest","throughput"],"description":"The order the key's requests try a model's routes in: cheapest (price), fastest (latency) or throughput (output tokens per second). Same price."},"retain_bodies":{"type":"boolean","description":"Whether request bodies are kept 7 days for replay."},"redact":{"type":"string","enum":["off","secrets","secrets_pii"],"description":"Secret redaction before a request is forwarded: off, secrets (API keys and tokens), or secrets_pii (also emails, phone numbers, card numbers, US SSNs and IBANs). Each value becomes a placeholder such as [[SECRET_1]] and is restored in the answer. x-uplink-redact overrides it per request."},"response_cache_ttl":{"type":["integer","null"],"description":"The response cache is on by default for this key, for this many seconds (60 to 86,400); null when off. x-uplink-cache: off skips it for one request."},"context_overflow":{"type":"string","enum":["error","middle-out"],"description":"A prompt longer than the model's context window: error sends it as it is; middle-out drops messages from the middle until it fits."},"autopilot_model":{"type":["string","null"],"description":"The cheaper model that answers near the monthly limit, or null."},"session_max_requests":{"type":["integer","null"],"description":"Requests one session (x-session-id) may make in any sliding hour, or null for no cap. Over it: 429 session_cap."},"session_max_spend_micro":{"type":["integer","null"],"description":"Micro-dollars one session may be charged per UTC day, counted from when the cap was set, or null. Once reached: 429 session_cap until the next UTC day."},"user_budget_micro":{"type":["integer","null"],"description":"Micro-dollars one end user (the x-customer-id header, else the `user` body field) may be charged on this key per user_budget_period, or null for none. Once reached: 402 user_budget_reached for that user until the period resets; other users go on."},"user_budget_period":{"type":"string","enum":["daily","weekly","monthly"],"description":"When each user's budget resets: 00:00 UTC each day, Monday 00:00 UTC each week, or the first of each UTC month (the default)."},"loop_guard":{"type":"boolean","description":"Refuse a session's sixth identical tool call (same name and arguments) or identical request body within 10 minutes with 429 loop_detected. On by default for hosted agents' keys."},"guardrails":{"oneOf":[{"$ref":"#/components/schemas/Guardrails"},{"type":"null"}],"description":"The key's own guardrails; null for none. The organization default (GET /v1/guardrails) layers under them."},"fallback_models":{"type":"array","items":{"type":"string"},"description":"Models tried in order when a request names no `models` of its own and its model fails. Empty for none."},"reveal_available":{"type":"boolean","description":"Whether the console can show the full key again."},"created_at":{"type":"integer","description":"Unix milliseconds."},"updated_at":{"type":["integer","null"]},"last_used_at":{"type":["integer","null"],"description":"Unix milliseconds, written at most once a minute."},"revoked_at":{"type":["integer","null"]},"expires_at":{"type":["integer","null"],"description":"Unix milliseconds, or null for never."}}},"KeyCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"1 to 64 characters."},"environment":{"type":"string","enum":["live","test"],"description":"live unless set."},"allowed_models":{"type":["array","null"],"items":{"type":"string"},"description":"Up to 100 model ids; the key may call nothing else."},"allowed_ips":{"type":["array","null"],"items":{"type":"string"},"description":"Up to 50 IPv4/IPv6 addresses or CIDRs (1.2.3.4, 10.0.0.0/8, 2001:db8::/32); the key answers 403 ip_not_allowed from anywhere else."},"rpm":{"type":"integer","description":"Requests per minute, 1 to 100,000."},"tpm":{"type":"integer","description":"Tokens per minute, 1 to 50,000,000."},"monthly_cap_usd":{"type":"number","description":"Dollars the key may spend per spend_limit_period (a UTC month unless set); the counter restarts at the start of each period."},"spend_limit_period":{"type":"string","enum":["daily","weekly","monthly"],"description":"monthly unless set. daily resets at 00:00 UTC, weekly at Monday 00:00 UTC."},"concurrency":{"type":"integer","description":"Requests this key may have in flight at once, 1 to 10,000. The console's name concurrency_limit is accepted too."},"budget_usd":{"type":"number","description":"The key's own prepaid budget in dollars, carved out of your balance. Once used up the key gets 402 key_budget_exhausted until it is raised."},"expires_at":{"type":"integer","description":"Unix milliseconds, in the future."},"routing":{"type":"string","enum":["cheapest","fastest","throughput"]},"retain_bodies":{"type":"boolean","description":"Keep request bodies 7 days for replay."},"session_max_requests":{"type":"integer","description":"Requests one session may make per sliding hour, 1 to 100,000."},"session_max_spend_usd":{"type":"number","description":"Dollars one session may be charged per UTC day. The console's session_max_spend_micro is accepted too."},"user_budget_usd":{"type":"number","description":"Dollars one end user (x-customer-id, else the `user` field) may be charged per user_budget_period. The console's user_budget_micro is accepted too. Past it that user gets 402 user_budget_reached."},"user_budget_period":{"type":"string","enum":["daily","weekly","monthly"],"description":"monthly unless set."},"loop_guard":{"type":"boolean","description":"Refuse a session's repeated identical tool call or body (more than 5 in 10 minutes)."},"redact":{"type":"string","enum":["off","secrets","secrets_pii"],"description":"Default off."},"fallback_models":{"$ref":"#/components/schemas/FallbackModels"},"response_cache_ttl":{"type":["integer","null"],"description":"Response cache on by default, for this many seconds (60 to 86,400); null or 0 for off."},"context_overflow":{"type":"string","enum":["error","middle-out"],"description":"error unless set."},"scope":{"type":"string","enum":["api"],"description":"Only api here; an admin key is created in the console."},"guardrails":{"oneOf":[{"$ref":"#/components/schemas/Guardrails"},{"type":"null"}],"description":"The key's own guardrails; null for none. The organization default (GET /v1/guardrails) layers under them."}}},"KeyPatch":{"type":"object","properties":{"name":{"type":"string"},"status":{"type":"string","enum":["active","revoked"],"description":"revoked pauses the key; active resumes it. A key revoked with DELETE cannot come back."},"allowed_models":{"type":["array","null"],"items":{"type":"string"}},"allowed_ips":{"type":["array","null"],"items":{"type":"string"},"description":"Addresses or CIDRs; null clears the allowlist."},"rpm":{"type":["integer","null"],"description":"null clears it."},"tpm":{"type":["integer","null"],"description":"null clears it."},"monthly_cap_usd":{"type":["number","null"],"description":"null clears it."},"spend_limit_period":{"type":"string","enum":["daily","weekly","monthly"]},"concurrency":{"type":["integer","null"],"description":"null clears it."},"budget_usd":{"type":["number","null"],"description":"Not under what the key has spent against its budget. null removes the budget; setting one again starts its count at zero."},"expires_at":{"type":["integer","null"],"description":"null clears it."},"routing":{"type":"string","enum":["cheapest","fastest","throughput"]},"retain_bodies":{"type":"boolean"},"session_max_requests":{"type":["integer","null"],"description":"null clears it."},"session_max_spend_usd":{"type":["number","null"],"description":"null clears it."},"user_budget_usd":{"type":["number","null"],"description":"null clears it."},"user_budget_period":{"type":"string","enum":["daily","weekly","monthly"]},"loop_guard":{"type":"boolean"},"redact":{"type":"string","enum":["off","secrets","secrets_pii"]},"fallback_models":{"type":["array","null"],"maxItems":5,"items":{"type":"string"},"description":"The key's default fallback list; [] or null clears it. Each must be a live model id or one of your aliases that the key's allowlist admits."},"response_cache_ttl":{"type":["integer","null"],"description":"60 to 86,400 seconds; null or 0 turns the key's default off."},"context_overflow":{"type":"string","enum":["error","middle-out"]},"guardrails":{"oneOf":[{"$ref":"#/components/schemas/Guardrails"},{"type":"null"}],"description":"The key's own guardrails; null for none. The organization default (GET /v1/guardrails) layers under them."}}},"Guardrails":{"type":"object","description":"Opt-in checks on your own traffic. Nothing applies unless set. A blocked request answers 400 guardrail_blocked before any model is asked and costs nothing; a flagged one carries `x-uplink-guardrail: flagged`; a stripped tool call `x-uplink-guardrail: tool_call_stripped`.","properties":{"blocked_patterns":{"type":"array","items":{"$ref":"#/components/schemas/BlockedPattern"},"description":"Up to 50. Checked against the prompt's system, user and tool text (not earlier assistant turns). A string is shorthand for { pattern, action: block, flags: i }."},"prompt_injection":{"type":"string","enum":["off","flag","block"],"description":"A local heuristic detector, no model call: known override and jailbreak phrasings, chat-template and role markers inside user or tool text, instructions aimed at the model inside tool results, invisible Unicode tag characters. It matches wording: paraphrases, other languages and encoded payloads pass, and text quoting an attack is flagged like one."},"tool_policy":{"oneOf":[{"$ref":"#/components/schemas/ToolPolicy"},{"type":"null"}]}}},"BlockedPattern":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string","description":"A JavaScript regular expression, up to 512 characters. Backreferences, lookaround and a repeated group that holds a repeat or alternatives are refused."},"flags":{"type":"string","description":"Any of i, m, s, u. i unless set.","pattern":"^[imsu]{0,4}$"},"action":{"type":"string","enum":["block","flag"],"description":"block unless set."},"name":{"type":["string","null"],"description":"Named in the error message instead of the pattern's position."}}},"ToolPolicy":{"type":"object","properties":{"allow":{"type":["array","null"],"items":{"type":"string"},"description":"Only these tools; null for every tool not denied. A trailing * matches a prefix."},"deny":{"type":"array","items":{"type":"string"},"description":"Never these: function names, server tools (web_search) and /mcp/org names (github__delete_*). Their declarations are removed before the request is forwarded, a tool_choice forcing one is refused, and a call to one in an answer is stripped."}}},"GuardrailsEnvelope":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/Guardrails"},{"type":"null"}]}}},"AuthKeyExchange":{"type":"object","required":["code","code_verifier"],"properties":{"code":{"type":"string","description":"The code from the callback URL."},"code_verifier":{"type":"string","description":"The verifier whose S256 hash was the code_challenge: 43 to 128 characters of A-Z, a-z, 0-9 and -._~."},"code_challenge_method":{"type":"string","enum":["S256"]},"callback_url":{"type":"string","description":"Optional; when sent it must be the approved callback URL exactly."}}},"AuthKeyExchanged":{"type":"object","required":["key","key_id","name","limit_usd"],"properties":{"key":{"type":"string","description":"The new API key (sk-relay-live-...)."},"key_id":{"type":"string","description":"Its id, for the key API and the console."},"name":{"type":"string","description":"The key_label the user approved."},"limit_usd":{"type":["number","null"],"description":"The key's budget in dollars, or null for none."}}},"Webhook":{"type":"object","required":["id","url","events","created_at"],"properties":{"id":{"type":"string","description":"wh_ and 24 hex characters."},"url":{"type":"string","description":"The https endpoint."},"events":{"type":"array","items":{"type":"string"},"description":"The events it receives: every account event."},"created_at":{"type":"integer","description":"Unix milliseconds."}}},"WebhookCreate":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"A public https URL."}}},"WebhookCreated":{"type":"object","required":["data"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"The signing secret, shown this once."}}}]}}},"WebhookList":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}},"SearchRequest":{"type":"object","required":["query"],"properties":{"query":{"type":"string","description":"Up to 400 characters."},"max_results":{"type":"integer","minimum":1,"maximum":10,"description":"1 to 10; 5 when left out."},"freshness":{"type":["string","null"],"enum":["day","week","month","year",null],"description":"Only pages published inside this window."}}},"SearchResponse":{"type":"object","required":["object","id","created","query","results","usage","charge_usd"],"properties":{"object":{"const":"search"},"id":{"type":"string","description":"The request id; GET /v1/requests/{request_id}/receipt does not serve it, the x-uplink-receipt header is the receipt."},"created":{"type":"integer","description":"Unix seconds."},"query":{"type":"string"},"results":{"type":"array","items":{"type":"object","required":["title","url","snippet","published"],"properties":{"title":{"type":"string"},"url":{"type":"string","format":"uri"},"snippet":{"type":"string"},"published":{"type":["string","null"],"description":"The page's publication date as the index has it, or null."}}}},"usage":{"type":"object","required":["searches"],"properties":{"searches":{"const":1}}},"charge_usd":{"type":"string","description":"Exact decimal dollars as a string, for example \"0.001421\"."}}},"WebhookDelivery":{"type":"object","required":["id","webhook_id","event","attempts","status","last_status_code","created_at","done_at"],"properties":{"id":{"type":"string"},"webhook_id":{"type":"string"},"event":{"type":"string","description":"balance.low, balance.empty, key.budget_reached, key.limit_reached, topup.credited, payment.failed, incident.opened or incident.resolved."},"attempts":{"type":"integer","description":"Attempts made so far, at most 3."},"status":{"type":"string","enum":["pending","delivered","failed"]},"last_status_code":{"type":["integer","null"],"description":"The last HTTP status; null when the endpoint did not answer."},"created_at":{"type":"integer","description":"Unix milliseconds."},"done_at":{"type":["integer","null"],"description":"Unix milliseconds, once delivered or given up."}}},"Invoice":{"type":"object","required":["number","payment_id","amount_micro","refunded_micro","bonus_micro","method","created_at","pdf_url"],"properties":{"number":{"type":"string","description":"INV-<year>-<n>."},"payment_id":{"type":"string","description":"The payment the invoice is for, as GET /v1/billing/payments lists it."},"amount_micro":{"type":"integer","description":"What was paid, in micro-dollars."},"refunded_micro":{"type":"integer","description":"Refunded since, in micro-dollars; 0 for none."},"bonus_micro":{"type":"integer","description":"Bonus credit that came with the payment, in micro-dollars, never charged."},"method":{"type":"string","description":"Card or Crypto."},"created_at":{"type":"integer","description":"When the payment was credited, Unix milliseconds."},"pdf_url":{"type":"string","description":"The path of the PDF: /v1/invoices/<number>.pdf."}}},"BillingDetails":{"type":"object","properties":{"company":{"type":["string","null"],"description":"The customer's name on the invoice; the organization's name when null."},"address_lines":{"type":"array","items":{"type":"string"},"description":"Up to 4 lines."},"country":{"type":["string","null"]},"tax_id":{"type":["string","null"],"description":"VAT, GST, EIN or any tax number, printed as given."},"email":{"type":["string","null"],"description":"Printed on the invoice; the organization's email when null."}}},"WebhookDeliveryList":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}}},"McpServer":{"type":"object","required":["id","name","url","header_names","tool_allowlist","enabled","prefix","created_at","updated_at"],"properties":{"id":{"type":"string","description":"mcp_ and 24 hex characters."},"name":{"type":"string","description":"The namespace: its tools are `name__tool` on /mcp/org."},"url":{"type":"string","description":"The server's https Streamable HTTP endpoint."},"header_names":{"type":"array","items":{"type":"string"},"description":"The names of the stored headers. Values are never returned."},"tool_allowlist":{"type":["array","null"],"items":{"type":"string"},"description":"The tools /mcp/org exposes, by the server's own names; null for all."},"enabled":{"type":"boolean"},"prefix":{"type":"string","description":"name followed by __."},"created_at":{"type":"integer","description":"Unix milliseconds."},"updated_at":{"type":"integer","description":"Unix milliseconds."}}},"McpServerCreate":{"type":"object","required":["name","url"],"properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,31}$","description":"No double underscore, not ending in _."},"url":{"type":"string","description":"A public https URL."},"headers":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"Up to 20, sent on every call, for example Authorization. Stored sealed."},"tool_allowlist":{"type":["array","null"],"items":{"type":"string"}},"enabled":{"type":"boolean"}}},"McpServerPatch":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"headers":{"type":["object","null"],"additionalProperties":{"type":"string"}},"tool_allowlist":{"type":["array","null"],"items":{"type":"string"}},"enabled":{"type":"boolean"}}},"McpServerEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/McpServer"}}},"McpServerList":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/McpServer"}}}},"McpCallList":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ts":{"type":"integer","description":"Unix milliseconds."},"server":{"type":"string"},"tool":{"type":"string"},"status":{"type":"string","enum":["ok","tool_error","failed","denied","blocked"]},"http_status":{"type":["integer","null"]},"latency_ms":{"type":"integer"},"error":{"type":["string","null"]},"key_id":{"type":["string","null"]},"arguments":{"description":"The call's arguments, only when the key keeps bodies; else null."}}}}}},"KeyEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Key"}}},"Session":{"type":"object","required":["id","requests","first_at","last_at","duration_ms","charge_micro","cost_usd","input_tokens","output_tokens","errors","models","key_ids"],"properties":{"id":{"type":"string"},"requests":{"type":"integer"},"first_at":{"type":"integer","description":"Unix milliseconds: when the first request started."},"last_at":{"type":"integer","description":"Unix milliseconds: when the last request ended."},"duration_ms":{"type":"integer"},"charge_micro":{"type":"integer","description":"Micro-dollars charged."},"cost_usd":{"type":"number"},"input_tokens":{"type":"integer","description":"Billed prompt tokens, cached reads and writes included."},"output_tokens":{"type":"integer"},"errors":{"type":"integer","description":"Requests the model failed or refused; never charged."},"models":{"type":"array","items":{"type":"string"}},"key_ids":{"type":"array","items":{"type":"string"}}}},"SessionList":{"type":"object","required":["data","days","has_more","next_before"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Session"}},"days":{"type":"integer"},"has_more":{"type":"boolean"},"next_before":{"type":["integer","null"]}}},"SessionStep":{"type":"object","required":["request_id","started_at","ended_at","key_id","model","outcome","status","input_tokens","cache_read_tokens","cache_write_tokens","output_tokens","charge_micro","cost_usd","latency_ms"],"properties":{"request_id":{"type":"string"},"started_at":{"type":"integer"},"ended_at":{"type":"integer"},"key_id":{"type":"string"},"model":{"type":"string"},"outcome":{"type":"string","enum":["completed","client_disconnected","upstream_error","upstream_rejected"]},"status":{"type":"integer"},"input_tokens":{"type":"integer"},"cache_read_tokens":{"type":"integer"},"cache_write_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"charge_micro":{"type":"integer"},"cost_usd":{"type":"number"},"latency_ms":{"type":"integer"},"refund_reason":{"type":["string","null"]},"tool_calls":{"type":["integer","null"],"description":"Server tool calls (web search, fetch, MCP, sandbox) whose results this request carried; null for none."}}},"SessionEnvelope":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","first_at","last_at","duration_ms","models","totals","truncated","steps"],"properties":{"id":{"type":"string"},"first_at":{"type":["integer","null"]},"last_at":{"type":["integer","null"]},"duration_ms":{"type":"integer"},"models":{"type":"array","items":{"type":"string"}},"totals":{"type":"object","properties":{"requests":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"charge_micro":{"type":"integer"},"cost_usd":{"type":"number"},"errors":{"type":"integer"},"latency_ms":{"type":"integer"}}},"truncated":{"type":"boolean","description":"True when the session has more than 1,000 requests and only the latest 1,000 are shown."},"steps":{"type":"array","items":{"$ref":"#/components/schemas/SessionStep"}}}}}},"ExportDestination":{"type":"object","required":["id","kind","name","summary","key_ids","include_bodies","enabled","consecutive_failures","created_at"],"properties":{"id":{"type":"string","pattern":"^exp_[0-9a-f]{16}$"},"kind":{"type":"string","enum":["webhook","langfuse","otlp"]},"name":{"type":"string"},"summary":{"type":"object","description":"host, and public_key_prefix (langfuse) or header_names (otlp). Never a secret."},"key_ids":{"type":["array","null"],"items":{"type":"string"},"description":"Only these keys' requests, or null for every key."},"include_bodies":{"type":"boolean","description":"Send request bodies, for keys that keep them."},"enabled":{"type":"boolean"},"consecutive_failures":{"type":"integer"},"disabled_reason":{"type":["string","null"],"description":"failures (50 in a row) or manual."},"disabled_at":{"type":["integer","null"]},"last_success_at":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}}},"ExportDestinationDetail":{"type":"object","required":["id","kind","name","summary","key_ids","include_bodies","enabled","consecutive_failures","created_at"],"properties":{"id":{"type":"string","pattern":"^exp_[0-9a-f]{16}$"},"kind":{"type":"string","enum":["webhook","langfuse","otlp"]},"name":{"type":"string"},"summary":{"type":"object","description":"host, and public_key_prefix (langfuse) or header_names (otlp). Never a secret."},"key_ids":{"type":["array","null"],"items":{"type":"string"},"description":"Only these keys' requests, or null for every key."},"include_bodies":{"type":"boolean","description":"Send request bodies, for keys that keep them."},"enabled":{"type":"boolean"},"consecutive_failures":{"type":"integer"},"disabled_reason":{"type":["string","null"],"description":"failures (50 in a row) or manual."},"disabled_at":{"type":["integer","null"]},"last_success_at":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"deliveries":{"type":"array","description":"The last 50 delivery attempts, newest first.","items":{"type":"object","properties":{"delivery_id":{"type":"string"},"status":{"type":"string","enum":["delivered","retrying","failed"]},"http_status":{"type":["integer","null"]},"attempts":{"type":"integer"},"events":{"type":"integer"},"error":{"type":["string","null"]},"created_at":{"type":"integer"}}}}}},"ExportCreate":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","enum":["webhook","langfuse","otlp"]},"name":{"type":"string","description":"1 to 80 characters."},"url":{"type":"string","description":"webhook: a public https URL."},"public_key":{"type":"string","description":"langfuse: the project's public key."},"secret_key":{"type":"string","description":"langfuse: the project's secret key. Stored encrypted; never shown again."},"host":{"type":"string","description":"langfuse: https://cloud.langfuse.com unless set (us.cloud.langfuse.com, or your own https host)."},"endpoint":{"type":"string","description":"otlp: the OTLP/HTTP base URL; /v1/traces is added unless it is already there."},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"otlp: up to 10 headers sent with every delivery, for example an API key. Stored encrypted; only their names are shown."},"key_ids":{"type":["array","null"],"items":{"type":"string"},"description":"Only these keys' requests; every key unless set."},"include_bodies":{"type":"boolean","description":"Include request bodies for keys with retain_bodies on. Off unless set."}}},"ExportPatch":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"key_ids":{"type":["array","null"],"items":{"type":"string"}},"include_bodies":{"type":"boolean"},"url":{"type":"string"},"public_key":{"type":"string"},"secret_key":{"type":"string"},"host":{"type":"string"},"endpoint":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}}}},"KeyList":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Key"}}}},"KeyCreated":{"type":"object","required":["key","data"],"properties":{"key":{"type":"string","description":"The full key. Store it now; it is not returned again."},"data":{"$ref":"#/components/schemas/Key"}}},"KeyUsageWindow":{"type":"object","required":["requests","spend_micro","spend_usd"],"properties":{"requests":{"type":"integer","description":"Requests logged in the window, failures included."},"spend_micro":{"type":"integer","description":"Micro-dollars charged in the window."},"spend_usd":{"type":"number","description":"The same in dollars."}}},"KeyUsageEnvelope":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["key_id","today","month","lifetime","budget"],"properties":{"key_id":{"type":"string"},"today":{"$ref":"#/components/schemas/KeyUsageWindow"},"month":{"$ref":"#/components/schemas/KeyUsageWindow"},"lifetime":{"$ref":"#/components/schemas/KeyUsageWindow"},"by_user":{"type":"array","description":"With ?group_by=user: this UTC month per end user, top 100 by spend.","items":{"type":"object","required":["user","requests","spend_micro","spend_usd"],"properties":{"user":{"type":"string"},"requests":{"type":"integer"},"spend_micro":{"type":"integer"},"spend_usd":{"type":"number"}}}},"budget":{"type":["object","null"],"description":"Null when the key has no budget.","required":["budget_micro","spent_micro","remaining_micro","budget_usd","spent_usd","remaining_usd"],"properties":{"budget_micro":{"type":"integer"},"spent_micro":{"type":"integer"},"remaining_micro":{"type":"integer"},"budget_usd":{"type":"number"},"spent_usd":{"type":"number"},"remaining_usd":{"type":"number"}}}}}}},"Error":{"type":"object","description":"The error envelope on OpenAI-format routes and on every route outside the chat formats.","required":["error"],"properties":{"error":{"type":"object","required":["message","type","code"],"properties":{"message":{"type":"string","description":"What went wrong, in plain words. Never names a host."},"type":{"type":"string","description":"OpenAI's error class for the status: invalid_request_error, authentication_error, insufficient_quota, permission_error, not_found_error, rate_limit_error or api_error."},"param":{"type":["string","null"],"description":"Always null."},"code":{"type":"string","description":"A stable code, the ones named on each response: invalid_request, invalid_api_key, insufficient_balance, model_not_found, rate_limited, upstream_error and so on."}}}}},"AnthropicError":{"type":"object","description":"Errors on POST /v1/messages, in Anthropic's shape. The stable code is appended to the message in parentheses.","required":["type","error","request_id"],"properties":{"type":{"const":"error"},"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","description":"Anthropic's error type for the status."},"message":{"type":"string","description":"The message, then the code: \"The API key is not valid. (invalid_api_key)\"."}}},"request_id":{"type":"string","description":"The same value as x-uplink-request-id."}}},"GeminiError":{"type":"object","description":"Errors on the Gemini-format routes, in Google's shape.","required":["error"],"properties":{"error":{"type":"object","required":["code","message","status"],"properties":{"code":{"type":"integer","description":"The HTTP status."},"message":{"type":"string"},"status":{"type":"string","description":"Google's status name: INVALID_ARGUMENT, UNAUTHENTICATED, PERMISSION_DENIED, NOT_FOUND, RESOURCE_EXHAUSTED, UNAVAILABLE and so on."},"details":{"type":"array","items":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","description":"The stable code."}}}}}}}},"SimpleError":{"type":"object","description":"The envelope on GET /v1/statements/public-key.","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}},"ChatCompletionRequest":{"type":"object","description":"OpenAI Chat Completions request. Anything OpenAI accepts passes through unchanged.","required":["messages"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"models":{"$ref":"#/components/schemas/FallbackModels"},"preset":{"type":"string","description":"`@preset/<name>`: one of your presets, merged under this request with `model` in place of the preset's."},"messages":{"type":"array","items":{"type":"object","additionalProperties":true}},"stream":{"type":"boolean","description":"Stream the answer as server-sent events."},"stream_options":{"type":"object","description":"`include_usage: true` is added when streaming and you did not set it, so the usage chunk is hidden from you only if you did not ask for it. Usage you do get carries the tokens you are billed for.","additionalProperties":true},"max_tokens":{"type":"integer","description":"Caps the output and sizes the balance hold. Without it or max_completion_tokens, the hold assumes 4,096 output tokens."},"max_completion_tokens":{"type":"integer"},"response_format":{"type":"object","description":"`json_object` or `json_schema`: the answer is then billed only if it matches (pay only for valid output).","additionalProperties":true},"auto":{"type":"object","description":"With `model: \"auto\"`: `tier_max` caps the tier the auto router may pick (fast < balanced < frontier). The router picks from the tiers in GET /v1/catalog `auto_router`, classifying the request locally (no extra model call): a reasoning ask or a high reasoning effort, or tools with code, goes to frontier; tools, code blocks, images, a json_schema answer or a prompt over 16,000 tokens to balanced; the rest to fast. The answer's `model` and x-uplink-model name the model chosen, x-uplink-auto-tier its tier, and it bills at that model's price. With an `x-session-id` header the same model answers that session for 30 minutes from its last request.","properties":{"tier_max":{"type":"string","enum":["fast","balanced","frontier"]}}},"routing":{"oneOf":[{"type":"string","enum":["price","latency","throughput","cheapest","fastest"]},{"type":"object","properties":{"sort":{"type":"string","enum":["price","latency","throughput"]}}}],"description":"The order this request tries the model's routes in, over the key's own setting: price (cheapest for us first, the default), latency (quickest first token lately) or throughput (most output tokens per second lately). The price you pay is the same whichever route answers."},"provider":{"type":"object","description":"OpenRouter-style `{\"sort\": \"price\" | \"latency\" | \"throughput\"}`, read as `routing`. Other members are ignored.","additionalProperties":true},"transforms":{"type":"array","items":{"type":"string","enum":["middle-out"]},"description":"`[\"middle-out\"]`: when the prompt plus room for the answer is longer than the model's context window, whole messages are dropped from the middle until it fits. Kept: system messages, the first user message and the latest turn; a tool call and its results go together. x-uplink-compressed says how many were dropped. `[]` turns a key's context_overflow default off for this request. The field is never sent on to the model."}},"additionalProperties":true},"MessagesRequest":{"type":"object","description":"Anthropic Messages request. Anything Anthropic accepts passes through unchanged.","required":["messages"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"models":{"$ref":"#/components/schemas/FallbackModels"},"preset":{"type":"string","description":"`@preset/<name>`: one of your presets, merged under this request with `model` in place of the preset's."},"max_tokens":{"type":"integer","description":"Required by Anthropic unless a preset sets it. Caps the output and sizes the balance hold."},"messages":{"type":"array","items":{"type":"object","additionalProperties":true}},"system":{"type":["string","array"]},"stream":{"type":"boolean","description":"Stream the answer as server-sent events."},"auto":{"type":"object","description":"With `model: \"auto\"`: `tier_max` caps the tier the auto router may pick (fast < balanced < frontier). The router picks from the tiers in GET /v1/catalog `auto_router`, classifying the request locally (no extra model call): a reasoning ask or a high reasoning effort, or tools with code, goes to frontier; tools, code blocks, images, a json_schema answer or a prompt over 16,000 tokens to balanced; the rest to fast. The answer's `model` and x-uplink-model name the model chosen, x-uplink-auto-tier its tier, and it bills at that model's price. With an `x-session-id` header the same model answers that session for 30 minutes from its last request.","properties":{"tier_max":{"type":"string","enum":["fast","balanced","frontier"]}}},"routing":{"oneOf":[{"type":"string","enum":["price","latency","throughput","cheapest","fastest"]},{"type":"object","properties":{"sort":{"type":"string","enum":["price","latency","throughput"]}}}],"description":"The order this request tries the model's routes in, over the key's own setting: price (cheapest for us first, the default), latency (quickest first token lately) or throughput (most output tokens per second lately). The price you pay is the same whichever route answers."},"provider":{"type":"object","description":"OpenRouter-style `{\"sort\": \"price\" | \"latency\" | \"throughput\"}`, read as `routing`. Other members are ignored.","additionalProperties":true},"transforms":{"type":"array","items":{"type":"string","enum":["middle-out"]},"description":"`[\"middle-out\"]`: when the prompt plus room for the answer is longer than the model's context window, whole messages are dropped from the middle until it fits. Kept: system messages, the first user message and the latest turn; a tool call and its results go together. x-uplink-compressed says how many were dropped. `[]` turns a key's context_overflow default off for this request. The field is never sent on to the model."}},"additionalProperties":true},"GenerateContentRequest":{"type":"object","description":"Gemini generateContent request. The model is in the path. Anything Google accepts passes through unchanged.","required":["contents"],"properties":{"contents":{"type":"array","items":{"type":"object","additionalProperties":true}},"systemInstruction":{"type":"object","additionalProperties":true},"generationConfig":{"type":"object","description":"`maxOutputTokens` caps the output and sizes the balance hold.","additionalProperties":true}},"additionalProperties":true},"UplinkModelNote":{"type":"object","description":"What RelaySwitch adds to each model entry. Hosts are never named: RelaySwitch is the provider of record.","required":["developer"],"properties":{"developer":{"type":"string","description":"The model's developer, for example Anthropic."},"alias_of":{"type":"string","description":"On an alias entry: the model id the alias runs on."}}},"ModelListOpenAI":{"type":"object","description":"GET /v1/models with a Bearer token.","required":["object","data"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"type":"object","required":["id","object","owned_by","uplink"],"properties":{"id":{"type":"string","description":"The model id, or the alias."},"object":{"const":"model"},"owned_by":{"type":"string","description":"The developer, or \"alias\" on an alias entry."},"uplink":{"$ref":"#/components/schemas/UplinkModelNote"}}}}}},"ModelListAnthropic":{"type":"object","description":"GET /v1/models with x-api-key.","required":["data","has_more","first_id","last_id"],"properties":{"data":{"type":"array","items":{"type":"object","required":["type","id","display_name","uplink"],"properties":{"type":{"const":"model"},"id":{"type":"string"},"display_name":{"type":"string"},"uplink":{"$ref":"#/components/schemas/UplinkModelNote"}}}},"has_more":{"const":false},"first_id":{"type":["string","null"]},"last_id":{"type":["string","null"]}}},"ModelListGemini":{"type":"object","description":"GET /v1beta/models, or GET /v1/models with x-goog-api-key.","required":["models"],"properties":{"models":{"type":"array","items":{"type":"object","required":["name","displayName","uplink"],"properties":{"name":{"type":"string","description":"`models/` then the model id or alias."},"displayName":{"type":"string"},"uplink":{"$ref":"#/components/schemas/UplinkModelNote"}}}}}},"TokenPrices":{"type":"object","description":"Dollars per million tokens.","required":["unit","input","cache_read","output"],"properties":{"unit":{"const":"usd_per_million_tokens"},"input":{"type":"number"},"cache_read":{"type":["number","null"],"description":"Null when the model has no cached-input rate."},"output":{"type":"number"}}},"CatalogModel":{"type":"object","description":"One model of the token-priced catalog: chat, embedding, moderation, rerank or transcription (`type`). Prices are what you pay; the base cost is never here.","required":["id","display_name","developer","mode","wire_formats","type","endpoints","pricing","list_pricing","coop_discount_pct","context_window","max_output_tokens","input_modalities","release_date","tags"],"properties":{"id":{"type":"string","description":"The id to send as `model`."},"display_name":{"type":"string","description":"The marketing name, for example Claude Fable 5.1."},"developer":{"type":"string"},"tags":{"$ref":"#/components/schemas/ModelTags"},"mode":{"type":"string","enum":["managed","byok"],"description":"byok: served on your own provider key, stored in the console; managed: served on RelaySwitch's own routes."},"wire_formats":{"type":"array","items":{"type":"string","enum":["openai","anthropic","gemini"]},"description":"Which request formats the model takes. The price is the same in each."},"type":{"type":"string","enum":["chat","embedding","moderation","rerank","transcription"],"description":"What the model does, and so which route takes it."},"endpoints":{"type":"array","items":{"type":"string"},"description":"The routes that take the model."},"pricing":{"oneOf":[{"$ref":"#/components/schemas/TokenPrices"},{"type":"object","required":["unit","request_fee"],"properties":{"unit":{"const":"usd_per_request"},"request_fee":{"type":"number","description":"A flat fee per request, on byok routes."}}},{"type":"object","required":["unit","audio_minute"],"properties":{"unit":{"const":"usd_per_audio_minute"},"audio_minute":{"type":"number","description":"Dollars per minute of audio, billed by the second (transcription models)."}}}]},"list_pricing":{"oneOf":[{"$ref":"#/components/schemas/TokenPrices"},{"type":"null"}],"description":"The developer's published price, when one is on record."},"coop_discount_pct":{"type":"number","description":"Percent off this month from all customers' combined volume on the model; already in `pricing`. 0 when none."},"context_window":{"type":["integer","null"],"description":"The context window in tokens, as the developer publishes it. Null when no developer page states one."},"max_output_tokens":{"type":["integer","null"],"description":"The most output tokens one request returns, as the developer publishes it. Null when no developer page states one."},"input_modalities":{"oneOf":[{"type":"array","items":{"type":"string","enum":["text","image","audio","video"]}},{"type":"null"}],"description":"What the model takes as input, as the developer publishes it. Null when no developer page states it."},"file_input":{"type":"string","enum":["native","parsed"],"description":"How a document part (a chat `file` part, a Messages `document` block, a Responses `input_file`) reaches the model: `native` as sent, `parsed` converted to text first (see `file_parsing`)."},"release_date":{"type":["string","null"],"description":"When the developer released the model: YYYY-MM-DD, or YYYY-MM when only the month is published. Null when no developer page states it."},"supported_parameters":{"type":"array","items":{"type":"string","enum":["tools","tool_choice","response_format","structured_outputs","reasoning","stop","seed","temperature","top_p","max_tokens","logprobs"]},"description":"Request parameters verified for this model: every route that may serve it publishes that it takes them. Absent when that is not known for every route; a parameter missing from the list is unverified, not refused. Other parameters still pass through to the model as sent."}}},"RerankResponse":{"type":"object","required":["id","object","model","results","usage"],"properties":{"id":{"type":"string","description":"The request id."},"object":{"const":"list"},"model":{"type":"string"},"results":{"type":"array","items":{"type":"object","required":["index","relevance_score"],"properties":{"index":{"type":"integer","description":"The document's position in the request."},"relevance_score":{"type":"number","description":"Higher is more relevant."},"document":{"type":"object","properties":{"text":{"type":"string"}}}}}},"usage":{"type":"object","properties":{"prompt_tokens":{"type":"integer"},"total_tokens":{"type":"integer"}}}}},"Transcription":{"type":"object","required":["text","usage"],"properties":{"text":{"type":"string"},"task":{"type":"string","enum":["transcribe","translate"],"description":"verbose_json only."},"language":{"type":"string","description":"verbose_json only."},"duration":{"type":"number","description":"Seconds of audio; verbose_json only."},"segments":{"type":"array","description":"verbose_json only.","items":{"type":"object","properties":{"id":{"type":"integer"},"start":{"type":"number"},"end":{"type":"number"},"text":{"type":"string"}}}},"usage":{"type":"object","required":["type","seconds"],"properties":{"type":{"const":"duration"},"seconds":{"type":"integer","description":"Audio seconds billed."}}}}},"BatchFile":{"type":"object","required":["id","object","bytes","created_at","filename","purpose"],"properties":{"id":{"type":"string","pattern":"^file-[0-9a-f]{24}$"},"object":{"const":"file"},"bytes":{"type":"integer"},"created_at":{"type":"integer","description":"Unix seconds."},"expires_at":{"type":"integer","description":"Unix seconds: 30 days after upload."},"filename":{"type":"string"},"purpose":{"type":"string","enum":["batch","batch_output"]},"status":{"const":"processed"}}},"Batch":{"type":"object","required":["id","object","endpoint","input_file_id","completion_window","status","created_at","expires_at","request_counts"],"properties":{"id":{"type":"string","pattern":"^batch_[0-9a-f]{24}$"},"object":{"const":"batch"},"endpoint":{"type":"string"},"errors":{"oneOf":[{"type":"object"},{"type":"null"}]},"input_file_id":{"type":"string"},"completion_window":{"const":"24h"},"status":{"type":"string","enum":["validating","failed","in_progress","finalizing","completed","expired","cancelling","cancelled"]},"output_file_id":{"type":["string","null"],"description":"Set once the batch finishes, when any line answered 2xx."},"error_file_id":{"type":["string","null"],"description":"Set once the batch finishes, when any line failed or expired."},"created_at":{"type":"integer"},"in_progress_at":{"type":["integer","null"]},"expires_at":{"type":"integer"},"finalizing_at":{"type":["integer","null"]},"completed_at":{"type":["integer","null"]},"failed_at":{"type":["integer","null"]},"expired_at":{"type":["integer","null"]},"cancelling_at":{"type":["integer","null"]},"cancelled_at":{"type":["integer","null"]},"request_counts":{"type":"object","required":["total","completed","failed"],"properties":{"total":{"type":"integer"},"completed":{"type":"integer"},"failed":{"type":"integer"}}},"metadata":{"oneOf":[{"type":"object"},{"type":"null"}]}}},"ModelTags":{"type":"array","description":"What kind of build the model is; empty for a stock model. uncensored: an uncensored, abliterated or roleplay build (or a media model with its host's safety checker off); abliterated: refusals removed at the weight level; roleplay: made for character chat and story writing; security: set up for security work; own-brand: sold under RelaySwitch's own name.","items":{"type":"string","enum":["uncensored","abliterated","roleplay","security","own-brand"]}},"Catalog":{"type":"object","required":["object","generated_at","data","coop_tiers","auto_router","x402"],"properties":{"object":{"const":"list"},"generated_at":{"type":"string","format":"date-time"},"data":{"type":"array","items":{"$ref":"#/components/schemas/CatalogModel"}},"x402":{"type":["object","null"],"description":"Paying per request without a key (the x402 note above): the scheme (`checkout`), the network (`hidden`), the asset (`USD`) and the smallest payment in dollars. Null where it is off.","required":["scheme","network","asset","min_payment_usd"],"properties":{"scheme":{"type":"string"},"network":{"type":"string"},"asset":{"type":"string"},"min_payment_usd":{"type":"number"}}},"coop_tiers":{"type":"array","description":"Once all customers together pass `min_tokens` on a model in a UTC month, its price drops by `discount_pct`.","items":{"type":"object","required":["min_tokens","discount_pct"],"properties":{"min_tokens":{"type":"integer"},"discount_pct":{"type":"number"}}}},"file_parsing":{"type":"object","description":"Documents any model can read: sent to a model whose `file_input` is `parsed`, a document is converted to text first. Larger files are refused; longer output is cut with a note.","required":["available","formats","max_file_mb","max_pages","max_output_chars","usd_per_page"],"properties":{"available":{"type":"boolean"},"formats":{"type":"array","items":{"type":"string"},"description":"File extensions read."},"max_file_mb":{"type":"number"},"max_pages":{"type":"integer"},"max_output_chars":{"type":"integer"},"usd_per_page":{"type":"number","description":"0 while free."}}},"auto_router":{"type":"object","description":"Send `model: \"auto\"` (or one of `aliases`) and the router picks from these lists for each request; see the `auto` request field. Each model bills at its own price in `data`.","required":["id","aliases","tiers"],"properties":{"id":{"const":"auto"},"aliases":{"type":"array","items":{"type":"string"}},"tiers":{"type":"object","description":"Model ids per tier, in the order the router tries them.","properties":{"fast":{"type":"array","items":{"type":"string"}},"balanced":{"type":"array","items":{"type":"string"}},"frontier":{"type":"array","items":{"type":"string"}}}}}},"server_tools":{"type":"array","description":"The server-side tools a chat, Messages or Responses request can declare, their price and whether each can run now.","items":{"$ref":"#/components/schemas/ServerTool"}}}},"ServerTool":{"type":"object","required":["name","type","accepts","unit","price_usd","available"],"properties":{"name":{"type":"string","enum":["web_search","web_fetch","code_execution","mcp"]},"type":{"type":"string","description":"Our own tool type, e.g. `uplink:web_search`."},"accepts":{"type":"array","items":{"type":"string"},"description":"The standard tool types and fields that map to it."},"unit":{"type":"string","enum":["search","fetch","container_hour","call"]},"price_usd":{"type":"string","description":"USD per unit, six decimals."},"minimum_billed_seconds":{"type":"integer","description":"code_execution: each call is billed at least this long."},"languages":{"type":"array","items":{"type":"string","enum":["bash","javascript","python"]},"description":"code_execution: languages the sandbox runs (`language` in the tool call). python is 3.13 with numpy and requests."},"available":{"type":"boolean","description":"False while the tool cannot run; a call then answers the model with a tool error."}}},"MediaParam":{"type":"object","description":"One setting a media model takes, as `input.<name>`.","required":["type"],"properties":{"type":{"type":"string","enum":["string","number","integer","boolean","url","urls","strings","object","objects"]},"required":{"const":true},"values":{"type":"array","description":"The only values accepted, when the setting is a choice.","items":{"type":["string","number"]}},"default":{"type":["string","number","boolean"],"description":"What runs when the setting is left out."},"min":{"type":"number"},"max":{"type":"number"},"max_items":{"type":"integer","description":"For urls, strings and objects: the most items allowed."}}},"MediaModel":{"type":"object","required":["id","object","display_name","developer","kind","tags","endpoints","unit","params","pricing"],"properties":{"id":{"type":"string","description":"The id to send as `model`."},"object":{"const":"media_model"},"display_name":{"type":"string"},"developer":{"type":"string"},"kind":{"type":"string","enum":["image","video","audio","3d"]},"tags":{"$ref":"#/components/schemas/ModelTags"},"endpoints":{"type":"array","description":"The routes that take this model.","items":{"type":"string","enum":["/v1/images/generations","/v1/generations"]}},"unit":{"type":"string","enum":["image","second","minute","generation","1k_characters","1k_bytes"],"description":"What one unit of the price is."},"settles_on_completion":{"const":true,"description":"The output's length follows an input (a reference video, a driving audio track), so it is known only after the run. The longest output the settings allow is charged when the model accepts the task, and on success the charge comes down to the seconds the model made, at the same price per unit; the rest is given back to the balance."},"params":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/MediaParam"}},"pricing":{"type":"array","description":"Price tiers, first match wins. `when` names the settings that select the tier.","items":{"type":"object","required":["when","price_usd","list_usd"],"properties":{"when":{"type":"object","additionalProperties":true},"addon":{"const":true,"description":"Added on top of the matching base tier."},"price_usd":{"type":"string","description":"Dollars per unit, to four decimals, as a string."},"list_usd":{"type":["string","null"],"description":"The developer's own price per unit, when the developer publishes one."}}}}}},"MediaCatalog":{"type":"object","required":["object","data"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/MediaModel"}}}},"ImageGenerationRequest":{"type":"object","description":"OpenAI's image request. `size`, `quality` and `n` are mapped onto the model's own settings; `input` carries any other setting the model takes.","required":["model","prompt"],"properties":{"model":{"type":"string","description":"An image model id from GET /v1/catalog/media."},"prompt":{"type":"string"},"size":{"type":"string","description":"`WIDTHxHEIGHT` or `auto`. A model with fixed sizes gets the nearest aspect ratio and resolution it supports."},"quality":{"type":"string","description":"Mapped onto the model's quality setting when it has one."},"n":{"type":"integer","minimum":1,"description":"Images per request. A model that makes one image per request refuses more."},"response_format":{"type":"string","enum":["url","b64_json"]},"background":{"type":"string"},"output_format":{"type":"string"},"seed":{"type":"integer"},"negative_prompt":{"type":"string"},"input":{"type":"object","description":"Extra settings from the model's `params`, merged over the mapped ones.","additionalProperties":true}}},"ImageGenerationResponse":{"type":"object","required":["id","created","data"],"properties":{"id":{"type":"string","description":"The generation id, `gen_` then 24 hex characters. Also in x-uplink-generation-id."},"created":{"type":"integer","description":"Unix seconds."},"data":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"The file at /v1/files, kept 30 days."},"b64_json":{"type":"string","description":"The file's bytes, base64, when `response_format` is b64_json."}}}}}},"GenerationRequest":{"type":"object","required":["model","input"],"properties":{"model":{"type":"string","description":"A media model id from GET /v1/catalog/media."},"input":{"type":"object","description":"The model's settings, as its `params` describe them.","additionalProperties":true}}},"Generation":{"type":"object","required":["id","object","model","kind","status","progress","created","completed","outputs","error","charge"],"properties":{"id":{"type":"string","pattern":"^gen_[0-9a-f]{24}$"},"object":{"const":"generation"},"model":{"type":"string"},"kind":{"type":"string","enum":["image","video","audio","3d"]},"status":{"type":"string","enum":["queued","running","succeeded","failed"]},"progress":{"type":["integer","null"],"description":"Percent, when the model reports it. 100 once succeeded."},"created":{"type":"integer","description":"Unix seconds."},"completed":{"type":["integer","null"],"description":"Unix seconds, once finished."},"outputs":{"type":"array","items":{"type":"object","required":["url","content_type","bytes"],"properties":{"url":{"type":"string","description":"The file at /v1/files, kept 30 days."},"content_type":{"type":"string"},"bytes":{"type":"integer"}}}},"error":{"oneOf":[{"type":"null"},{"type":"object","required":["message"],"properties":{"message":{"type":"string"}}}],"description":"Set once failed."},"charge":{"type":"object","required":["amount_usd","units","unit","refunded"],"properties":{"amount_usd":{"type":"string","description":"Dollars, to four decimals, as a string. 0.0000 once failed. For a model that settles on completion, the hold until it succeeds, then the settled charge."},"units":{"type":"number","description":"How many units were billed."},"unit":{"type":"string"},"refunded":{"type":"boolean","description":"True once failed: the charge was given back."},"hold_usd":{"type":"string","description":"Only for a model that settles on completion: what was charged when the model accepted the task, for its longest output."},"settled":{"type":"boolean","description":"Only for a model that settles on completion: true once it succeeded and `amount_usd` is final; the difference from `hold_usd` is back in the balance."}}}}},"EstimateRequest":{"type":"object","description":"Send `input_tokens`, or text to count: `prompt`, `system` and `messages` in any combination.","properties":{"prompt":{"type":"string"},"system":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"content":{"type":["string","array"]}},"additionalProperties":true}},"input_tokens":{"type":"integer","description":"Taken as given when present."},"output_tokens":{"type":"integer","description":"Default 1000."},"models":{"type":"array","description":"Limit the answer to these ids.","items":{"type":"string"}}}},"EstimateResponse":{"type":"object","required":["object","prices","input_tokens","output_tokens","basis","data"],"properties":{"object":{"const":"estimate"},"prices":{"type":"string","enum":["public","your organization"]},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"basis":{"type":"string","description":"How the input count was arrived at."},"data":{"type":"array","description":"Metered models only, cheapest first.","items":{"type":"object","required":["model","display_name","developer","input_tokens","output_tokens","cost_usd","list_cost_usd","saved_usd","saved_pct"],"properties":{"model":{"type":"string"},"display_name":{"type":"string"},"developer":{"type":"string"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"cost_usd":{"type":"number"},"list_cost_usd":{"type":["number","null"]},"saved_usd":{"type":["number","null"]},"saved_pct":{"type":["integer","null"]}}}}}},"RequestRecord":{"type":"object","required":["object","id","created_at","key_id","model","status","outcome","usage","charge_usd","list_cost_usd","saved_usd","refund","latency_ms"],"properties":{"object":{"const":"request"},"id":{"type":"string","description":"The x-uplink-request-id."},"created_at":{"type":"string","format":"date-time"},"key_id":{"type":"string"},"model":{"type":"string","description":"The model that answered, after any alias."},"status":{"type":"integer","description":"The HTTP status the model's route answered with."},"outcome":{"type":"string","description":"completed, client_disconnected, cache_hit (answered from the response cache), upstream_error and so on."},"usage":{"type":"object","required":["input_tokens","cache_read_tokens","cache_write_tokens","output_tokens","source"],"properties":{"input_tokens":{"type":"integer"},"cache_read_tokens":{"type":"integer"},"cache_write_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"source":{"type":"string","description":"Where the counts came from: the model's own usage report, or an estimate."}}},"charge_usd":{"type":"string","description":"Exact decimal dollars as a string, for example \"0.001421\"."},"list_cost_usd":{"type":["string","null"],"description":"The same tokens at the developer's list price, when one is on record."},"saved_usd":{"type":["string","null"],"description":"list_cost_usd minus charge_usd, never below zero."},"refund":{"type":["string","null"],"description":"refusal or invalid_output when the answer was not billed."},"latency_ms":{"type":"integer"}}},"ReceiptPayload":{"type":"object","description":"What a signed receipt states. The signature is over the UTF-8 bytes of the `payload` string, which is this object serialized.","required":["type","v","request_id","created_at","model","billed_tokens","charge_usd","refund","request_sha256","response_sha256"],"properties":{"type":{"const":"uplinkswitch.receipt"},"v":{"const":1},"request_id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"model":{"type":"string","description":"The model that answered, after any alias."},"billed_tokens":{"type":"object","required":["input","cache_read","cache_write","output"],"properties":{"input":{"type":"integer"},"cache_read":{"type":"integer"},"cache_write":{"type":"integer"},"output":{"type":"integer"}}},"charge_usd":{"type":"string","description":"Exact decimal dollars as a string, for example \"0.001421\"."},"refund":{"oneOf":[{"type":"null"},{"type":"string","enum":["refusal","invalid_output"]}]},"request_sha256":{"type":["string","null"],"description":"Hex SHA-256 of the request body as sent."},"response_sha256":{"type":["string","null"],"description":"Hex SHA-256 of the response body as relayed."}}},"SignedReceipt":{"type":"object","required":["payload","signature","alg","key_id","public_key_url","receipt"],"properties":{"payload":{"type":"string","description":"The receipt as the exact JSON string that was signed."},"signature":{"type":"string","description":"Ed25519 signature over the payload's UTF-8 bytes, base64url."},"alg":{"const":"Ed25519"},"key_id":{"type":"string"},"public_key_url":{"type":"string"},"receipt":{"$ref":"#/components/schemas/ReceiptPayload"}}},"StatusFeed":{"type":"object","required":["generated_at","services","incidents","failover_rehearsals"],"properties":{"generated_at":{"type":"string","format":"date-time"},"incidents":{"type":"array","description":"The last 90 days of incidents and any still open, newest first. Opened by the hourly check when a service turns degraded or down, resolved when it is operational again.","items":{"type":"object","required":["id","service","severity","started_at","resolved_at","title","detail","post_mortem"],"properties":{"id":{"type":"string"},"service":{"type":"string","enum":["api","claude","gpt","gemini"]},"severity":{"type":"string","enum":["degraded","down"],"description":"The worst the incident reached."},"started_at":{"type":"integer","description":"Unix milliseconds."},"resolved_at":{"type":["integer","null"],"description":"Unix milliseconds; null while open."},"title":{"type":"string"},"detail":{"type":["string","null"],"description":"`status 503`, `no answer`, `elevated errors` or `requests failed`."},"post_mortem":{"type":["string","null"],"description":"Staff text, added after the fact."}}}},"services":{"type":"array","items":{"type":"object","required":["id","name","status","last_check_at","latency_ms","days","last_incident"],"properties":{"id":{"type":"string","enum":["api","claude","gpt","gemini"]},"name":{"type":"string"},"status":{"type":"string","enum":["operational","degraded","down","unknown"],"description":"down: the latest check failed. degraded: a check failed in the last 24 hours. unknown: no check in 30 days."},"last_check_at":{"type":["integer","null"],"description":"Unix milliseconds."},"latency_ms":{"type":["integer","null"]},"days":{"type":"array","description":"30 entries, oldest first, every day present.","items":{"type":"object","required":["date","checks","failures"],"properties":{"date":{"type":"string","format":"date"},"checks":{"type":"integer"},"failures":{"type":"integer"}}}},"last_incident":{"oneOf":[{"type":"null"},{"type":"object","required":["at","detail"],"properties":{"at":{"type":"integer","description":"Unix milliseconds."},"detail":{"type":["string","null"],"description":"`status 503` or `no answer`."}}}]}}}},"failover_rehearsals":{"type":"array","description":"The latest daily real request on each wire format's fallback route.","items":{"type":"object","required":["wire_format","ts","ok","latency_ms"],"properties":{"wire_format":{"type":"string","enum":["openai","anthropic","gemini"]},"ts":{"type":"integer","description":"Unix milliseconds."},"ok":{"type":"boolean"},"latency_ms":{"type":["integer","null"]}}}}}},"AppUsage":{"type":"object","required":["generated_at","windows"],"properties":{"generated_at":{"type":"string","format":"date-time"},"windows":{"type":"array","description":"One entry for the last 7 days and one for the last 30.","items":{"type":"object","required":["days","data"],"properties":{"days":{"type":"integer","enum":[7,30]},"data":{"type":"array","description":"Named apps, most tokens first, then Other.","items":{"type":"object","required":["app","requests","tokens"],"properties":{"app":{"type":"string","description":"A public tool's name, or Other for every app off the list."},"requests":{"type":"integer","description":"Answered requests in the window."},"tokens":{"type":"integer","description":"Tokens in and out, cache reads and writes included."}}}}}}}}},"Proof":{"type":"object","required":["generated_at","canary","data"],"properties":{"generated_at":{"type":"string","format":"date-time"},"canary":{"type":"string","description":"The fixed prompt every route is sent."},"data":{"type":"array","items":{"type":"object","required":["model","display_name","developer","checked_at","verdict","unchanged_since","last_change"],"properties":{"model":{"type":"string"},"display_name":{"type":"string"},"developer":{"type":"string"},"checked_at":{"type":["integer","null"],"description":"Unix milliseconds of the latest check that read the model."},"verdict":{"type":"string","enum":["stable","changed","unchecked"],"description":"stable: the fingerprint held. changed: it moved since the last check. unchecked: no check has read the model yet, or its hosts are not probed."},"unchanged_since":{"type":["integer","null"],"description":"Unix milliseconds."},"last_change":{"type":["integer","null"],"description":"Unix milliseconds."}}}}}},"PublicKey":{"type":"object","required":["key_id","alg","jwk","verify"],"properties":{"key_id":{"type":"string"},"alg":{"const":"Ed25519"},"jwk":{"type":"object","description":"The public JWK: kty OKP, crv Ed25519, x, with kid, use sig and alg EdDSA.","required":["kty","crv","x","kid","use","alg"],"properties":{"kty":{"const":"OKP"},"crv":{"const":"Ed25519"},"x":{"type":"string"},"kid":{"type":"string"},"use":{"const":"sig"},"alg":{"const":"EdDSA"}},"additionalProperties":true},"verify":{"type":"string","description":"How to check a signature."}}}},"headers":{"x-uplink-request-id":{"description":"On every response. Quote it when you write in; it finds the exact call.","schema":{"type":"string"}},"x-uplink-model":{"description":"The model that answered, after any alias, preset or fallback. It is the model billed.","schema":{"type":"string"}},"x-uplink-alias":{"description":"The alias the request named, when it named one.","schema":{"type":"string"}},"x-uplink-preset":{"description":"The preset the request named, when it named one.","schema":{"type":"string"}},"x-uplink-charge":{"description":"What the request cost you. Exact decimal dollars as a string, for example \"0.001421\". On non-streamed responses. The same figure is `usage.cost`, a number, in OpenAI- and Anthropic-format answers.","schema":{"type":"string"}},"x-uplink-list-cost":{"description":"The same tokens at the developer's list price, where one is on record. On non-streamed responses.","schema":{"type":"string"}},"x-uplink-saved":{"description":"x-uplink-list-cost minus x-uplink-charge, never below zero. On non-streamed responses.","schema":{"type":"string"}},"x-uplink-valid":{"description":"Whether the answer matched the JSON or schema the request asked for. Only when it asked.","schema":{"type":"string","enum":["true","false"]}},"x-uplink-refund":{"description":"This answer was not billed: the model refused, or the answer missed the required output.","schema":{"type":"string","enum":["refusal","invalid_output"]}},"x-uplink-receipt":{"description":"The signed receipt, compact: base64url(payload) \".\" base64url(signature). On non-streamed responses. GET /v1/requests/{request_id}/receipt has it as a document.","schema":{"type":"string"}},"x-uplink-autopilot":{"description":"The model the request asked for, when the key's budget autopilot answered with its cheaper model near the monthly limit.","schema":{"type":"string"}},"x-uplink-crowd-cache":{"description":"`on` when the gateway added a cache breakpoint to a large system prompt that many accounts send. Cached reads bill at the cached rate.","schema":{"type":"string","enum":["on"]}},"x-uplink-waste":{"description":"The share of the request that repeated earlier text or was a pasted blob, as a whole percent, when it is 10% or more.","schema":{"type":"string","pattern":"^[0-9]{1,3}%$"}},"x-uplink-routing":{"description":"`fastest` or `throughput` when the key or the request's `routing` set that route order.","schema":{"type":"string","enum":["fastest","throughput"]}},"x-uplink-auto-tier":{"description":"With `model: \"auto\"`: the tier the model came from. x-uplink-model names the model.","schema":{"type":"string","enum":["fast","balanced","frontier"]}},"x-uplink-attempts":{"description":"How many routes were tried before this answer: 1 is the first choice; more means a failover you did not see. The price is the same either way. 0 on a response-cache hit.","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-cache":{"description":"On a request that asked for the response cache: HIT (answered from it, billed a tenth of the original charge, usage counts zero), MISS (answered by the model and kept when it succeeds) or BYPASS (not cacheable: a server tool, or an endpoint other than chat completions, messages and embeddings).","schema":{"type":"string","enum":["HIT","MISS","BYPASS"]}},"x-uplink-cache-ttl":{"description":"Seconds the answer is kept: the full TTL on a MISS, what is left of it on a HIT.","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-cache-age":{"description":"On a HIT: seconds since the answer was kept.","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-cache-source-id":{"description":"On a HIT: the request id that paid for the answer.","schema":{"type":"string"}},"x-uplink-compressed":{"description":"Messages middle-out dropped from the middle of the prompt to fit the model's context window (transforms or the key's context_overflow).","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-redactions":{"description":"How many secrets (and with secrets_pii, personal values) were replaced by placeholders before the request was forwarded. Sent whenever redaction ran; the answer carries the originals back.","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-generation-id":{"description":"The media generation this response belongs to.","schema":{"type":"string","pattern":"^gen_[0-9a-f]{24}$"}},"x-uplink-charge-usd":{"description":"What the generation, edit, transcription or rerank cost you, dollars to four decimals.","schema":{"type":"string"}},"x-uplink-tool-calls":{"description":"Server tools: how many tool calls ran in this request.","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-tool-charge":{"description":"Server tools: the tool fees of this request. Exact decimal dollars as a string, for example \"0.001421\".","schema":{"type":"string"}},"x-uplink-tool-rounds":{"description":"Server tools: how many model rounds ran.","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-round-ids":{"description":"Server tools: every model round's request id, comma-separated; the last is x-uplink-request-id. Each has its own receipt.","schema":{"type":"string"}},"x-uplink-total-charge":{"description":"Server tools: every round's x-uplink-charge plus the tool fees. Exact decimal dollars as a string, for example \"0.001421\".","schema":{"type":"string"}},"x-uplink-sandbox-session":{"description":"Server tools: the code sandbox session; send it back as the code tool's `session_id` to keep its files.","schema":{"type":"string"}}},"parameters":{"x-customer-id":{"name":"x-customer-id","in":"header","description":"The end user of your app this request is for (1 to 128 visible ASCII characters); the OpenAI `user` body field does the same when this header is absent. Stored on the request as `user` for the console's request log, the usage CSV, GET /v1/usage/summary by_user and GET /v1/keys/{key_id}/usage?group_by=user, and counted against the key's user_budget.","schema":{"type":"string","pattern":"^[\\x21-\\x7e]{1,128}$"}},"x-session-id":{"name":"x-session-id","in":"header","description":"Groups requests into a session (1 to 128 visible ASCII characters) for GET /v1/sessions, log exports and the key's session caps and loop guard; `x-uplink-session`, or a `session_id` or `user` body field, does the same when this header is absent. With `model: \"auto\"` the same session id also gets the same model for 30 minutes from its last request, so an agent keeps one model.","schema":{"type":"string","pattern":"^[\\x21-\\x7e]{1,128}$"}},"x-uplink-require":{"name":"x-uplink-require","in":"header","description":"`json`: bill only an answer that parses as JSON. A miss on a non-streamed request is retried once and the missed answer is free. A JSON schema or JSON mode in the request body does the same without the header.","schema":{"type":"string","enum":["json"]}},"x-uplink-cache":{"name":"x-uplink-cache","in":"header","description":"`on`: answer an exact repeat of an earlier request from your organization (same model and body; stream, user and metadata aside) from the response cache, for a tenth of the original charge. `off` skips a key's response_cache_ttl default for this request. Chat completions, messages and embeddings, streamed or not; never a request with a server tool. OpenRouter's x-openrouter-cache headers work too.","schema":{"type":"string","enum":["on","off","true","false"]}},"x-uplink-cache-ttl":{"name":"x-uplink-cache-ttl","in":"header","description":"Seconds to keep this answer, 60 to 86,400; 300 unless set (or the key's response_cache_ttl).","schema":{"type":"string","pattern":"^[0-9]+$"}},"x-uplink-cache-clear":{"name":"x-uplink-cache-clear","in":"header","description":"`true`: drop the kept answer and ask the model again; the new answer is kept.","schema":{"type":"string","enum":["true"]}},"x-uplink-crowd-cache":{"name":"x-uplink-crowd-cache","in":"header","description":"`off`: leave this request out of shared prompt caching. No cache breakpoint is added and its system prompt is not counted.","schema":{"type":"string","enum":["off"]}},"x-uplink-app":{"name":"x-uplink-app","in":"header","description":"The slug of an approved app, when a request is made through one. Records the app on the request; never touches the price.","schema":{"type":"string"}},"x-uplink-redact":{"name":"x-uplink-redact","in":"header","description":"Overrides the key's redact setting for this request. `secrets` replaces API keys, tokens and private keys in the message text with placeholders such as [[SECRET_1]] before the request is forwarded; `secrets_pii` also replaces emails, phone numbers, card numbers, US SSNs and IBANs. The answer gets the originals back. `off` sends the text as written.","schema":{"type":"string","enum":["off","secrets","secrets_pii"]}},"x-uplink-max-tool-rounds":{"name":"x-uplink-max-tool-rounds","in":"header","description":"Server tools: the most tool rounds this request may run, 1 to 8 (default 8). The body's `max_tool_rounds` does the same.","schema":{"type":"integer","minimum":1,"maximum":8}},"anthropic-version":{"name":"anthropic-version","in":"header","description":"Passed through to the model. The Anthropic SDK sends it.","schema":{"type":"string","examples":["2023-06-01"]}},"request_id":{"name":"request_id","in":"path","required":true,"description":"The x-uplink-request-id of a finished request.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},"invoice_number":{"name":"invoice_number","in":"path","required":true,"description":"An invoice's `number`, as GET /v1/invoices lists it.","schema":{"type":"string","pattern":"^INV-[0-9]{4}-[0-9]{1,9}$"}},"destination_id":{"name":"destination_id","in":"path","required":true,"description":"An export destination's `id`.","schema":{"type":"string","pattern":"^exp_[0-9a-f]{16}$"}},"server_id":{"name":"server_id","in":"path","required":true,"description":"An MCP server's `id`, as GET /v1/mcp-servers lists it.","schema":{"type":"string","pattern":"^mcp_[0-9a-f]{24}$"}},"preset_name":{"name":"name","in":"path","required":true,"description":"The preset's name, without `@preset/`.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,63}$"}},"key_id":{"name":"key_id","in":"path","required":true,"description":"A key's `id`, as GET /v1/keys lists it.","schema":{"type":"string","pattern":"^[0-9a-hj-km-np-tv-z]{12}$"}},"generation_id":{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","pattern":"^gen_[0-9a-f]{24}$"}},"file_id":{"name":"file_id","in":"path","required":true,"description":"A batch file's id.","schema":{"type":"string","pattern":"^file-[0-9a-f]{24}$"}},"batch_id":{"name":"batch_id","in":"path","required":true,"schema":{"type":"string","pattern":"^batch_[0-9a-f]{24}$"}},"file_path":{"name":"path","in":"path","required":true,"description":"`{date}/{generation_id}/{n}.{ext}`, exactly as a generation's `outputs` give it.","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}/gen_[0-9a-f]{24}/\\d{1,2}\\.[a-z0-9]{2,5}$"}}},"responses":{}}}