{"openapi":"3.1.0","info":{"title":"UplinkSwitch API","version":"2026-10-12","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.uplinkswitch.com/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, and the model id when the request named one of your aliases.\n\nAuthentication: `Authorization: Bearer sk-uplink-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) 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\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`.","contact":{"url":"https://uplinkswitch.com/contact","email":"hello@uplinkswitch.com"}},"servers":[{"url":"https://api.uplinkswitch.com","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":"receipts","description":"What a finished request cost, and the signed proof of it."},{"name":"status","description":"Public feeds: service status 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.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"}],"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-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"}},"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, or the model is served in another wire format.","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: the key's monthly spend limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. credential_required: the model runs on your own provider key and none is stored. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists every model id.","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"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute or concurrency. retry-after says when.","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.","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/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.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"}],"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-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"}},"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, or the model is served in another wire format.","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: the key's monthly spend limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. credential_required: the model runs on your own provider key and none is stored. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists every model id.","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"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute or concurrency. retry-after says when.","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.","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/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.","tags":["chat"],"parameters":[{"$ref":"#/components/parameters/x-uplink-require"},{"$ref":"#/components/parameters/x-uplink-crowd-cache"},{"$ref":"#/components/parameters/x-uplink-app"}],"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-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"}},"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, or the model is served in another wire format.","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: the key's monthly spend limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. credential_required: the model runs on your own provider key and none is stored. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists every model id.","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"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute or concurrency. retry-after says when.","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.","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/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"}],"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-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"}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request: not a JSON object, no model named, or the model is served in another wire format.","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: the key's monthly spend limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. credential_required: the model runs on your own provider key and none is stored. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists every model id.","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"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute or concurrency. retry-after says when.","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.","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. 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.","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/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-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"}},"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, or the model is served in another wire format.","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: the key's monthly spend limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. credential_required: the model runs on your own provider key and none is stored. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists every model id.","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"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute or concurrency. retry-after says when.","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.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicError"}}}}}}},"/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"}],"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-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"}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request: not a JSON object, no model named, or the model is served in another wire format.","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: the key's monthly spend limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. credential_required: the model runs on your own provider key and none is stored. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists every model id.","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"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute or concurrency. retry-after says when.","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.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now.","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"}],"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-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"}},"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, or the model is served in another wire format.","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: the key's monthly spend limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"403":{"description":"scope_denied: the key's allowlist does not name this model. credential_required: the model runs on your own provider key and none is stored. account_frozen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"404":{"description":"model_not_found. GET /v1/catalog lists every model id.","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"}}}},"429":{"description":"rate_limited: the key's requests per minute, tokens per minute or concurrency. retry-after says when.","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.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeminiError"}}}},"503":{"description":"upstream_unavailable: no route for this model is answering right now.","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. 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, or at https://uplinkswitch.com/statements/verify.","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/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/status":{"get":{"operationId":"getStatus","summary":"Service status, last 30 days","description":"One entry per public service from the hourly probes, with a day-by-day count, plus 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/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/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"}}}}}}},"/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-uplink-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."}},"schemas":{"ModelId":{"type":"string","description":"A model id from GET /v1/catalog, or one of your organization's aliases. Not enumerated here."},"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":["model","messages"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"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.","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}},"additionalProperties":true},"MessagesRequest":{"type":"object","description":"Anthropic Messages request. Anything Anthropic accepts passes through unchanged.","required":["model","max_tokens","messages"],"properties":{"model":{"$ref":"#/components/schemas/ModelId"},"max_tokens":{"type":"integer","description":"Required by Anthropic. 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."}},"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 UplinkSwitch adds to each model entry. Hosts are never named: UplinkSwitch 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 chat model. Prices are what you pay; the base cost is never here.","required":["id","display_name","developer","mode","wire_formats","pricing","list_pricing","coop_discount_pct"],"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"},"mode":{"type":"string","enum":["managed","byok"],"description":"byok: served on your own provider key, stored in the console; managed: served on UplinkSwitch'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."},"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."}}}]},"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."}}},"Catalog":{"type":"object","required":["object","generated_at","data","coop_tiers"],"properties":{"object":{"const":"list"},"generated_at":{"type":"string","format":"date-time"},"data":{"type":"array","items":{"$ref":"#/components/schemas/CatalogModel"}},"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"}}}}}},"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","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"]},"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"],"description":"What one unit of the price is."},"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."},"units":{"type":"number","description":"How many units were billed."},"unit":{"type":"string"},"refunded":{"type":"boolean","description":"True once failed: the charge was given back."}}}}},"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, 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","failover_rehearsals"],"properties":{"generated_at":{"type":"string","format":"date-time"},"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"]}}}}}},"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."},"verdict":{"type":"string","enum":["stable","changed","failed","unchecked"],"description":"stable: the fingerprint held. changed: it moved since the last check. failed: the check could not run. unchecked: never checked."},"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.","schema":{"type":"string"}},"x-uplink-alias":{"description":"The alias 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.","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` when the key is set to fastest routing.","schema":{"type":"string","enum":["fastest"]}},"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 cost you, dollars to four decimals.","schema":{"type":"string"}}},"parameters":{"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-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"}},"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}$"}},"generation_id":{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","pattern":"^gen_[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":{}}}