{"openapi":"3.1.0","info":{"title":"Kunavo API","version":"1.1.0","summary":"OpenAI-compatible AI gateway — chat, image, video, speech and music models behind one key.","description":"Kunavo is an OpenAI-wire-compatible API gateway. If your code can call the\nOpenAI API, it can call Kunavo by changing only the base URL.\n\nAuthenticate every request with `Authorization: Bearer <KUNAVO_API_KEY>`.\n\n`GET /v1/models` is the authoritative catalog. Each entry carries a\n`kunavo.endpoint` field telling you which HTTP endpoint to POST to —\nroute on that rather than hardcoding slugs, so models that ship or retire\nare picked up automatically.\n\nA prose reference for autonomous agents lives at https://kunavo.com/llms.txt.","termsOfService":"https://kunavo.com/legal/terms","contact":{"name":"Kunavo support","email":"contact@kunavo.com","url":"https://kunavo.com/about"},"license":{"name":"Proprietary","url":"https://kunavo.com/legal/terms"}},"externalDocs":{"description":"Quickstart","url":"https://kunavo.com/docs/quickstart"},"servers":[{"url":"https://api.kunavo.com/v1","description":"Production"}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"tags":[{"name":"Models","description":"Catalog discovery."},{"name":"Chat","description":"Text generation."},{"name":"Images","description":"Text-to-image and image editing."},{"name":"Video","description":"Video generation, synchronous and asynchronous."},{"name":"Audio","description":"Speech, transcription and music."},{"name":"Embeddings","description":"Vector embeddings."},{"name":"Files","description":"Source-asset hosting."},{"name":"Usage","description":"The calling account's own request, token and cost totals."}],"paths":{"/models":{"get":{"tags":["Models"],"operationId":"listModels","summary":"List every enabled model","description":"Returns the live catalog. Entries are OpenAI-shaped plus a `context_length` and a `kunavo` object carrying category, endpoint, capabilities, context window, output cap and billing unit.","responses":{"200":{"description":"The current catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Model"}}}}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/chat/completions":{"post":{"tags":["Chat"],"operationId":"createChatCompletion","summary":"Create a chat completion","description":"OpenAI Chat Completions, fully compatible. Works for every model whose `kunavo.endpoint` is `chat`. Set `stream: true` for SSE chunks terminated by `data: [DONE]`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionRequest"},"example":{"model":"claude-fable-5","messages":[{"role":"user","content":"Hello"}]}}}},"responses":{"200":{"description":"A chat completion. When `stream` is true the body is an SSE stream of `chat.completion.chunk` events instead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletion"}},"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/responses":{"post":{"tags":["Chat"],"operationId":"createResponse","summary":"Create a response (OpenAI Responses API)","description":"Native OpenAI Responses surface, available for every `chat` model. This is the wire API OpenAI Codex CLI requires (`wire_api = \"responses\"`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"model":"claude-fable-5","input":"Hello"}}}},"responses":{"200":{"description":"A response object, OpenAI-shaped.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/messages":{"post":{"tags":["Chat"],"operationId":"createMessage","summary":"Create a message (Anthropic Messages API)","description":"Native Anthropic Messages surface, for Claude-family models. Use when a client speaks Messages rather than Chat Completions.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"model":"claude-fable-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}}}},"responses":{"200":{"description":"A Messages-shaped response.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/images/generations":{"post":{"tags":["Images"],"operationId":"createImage","summary":"Generate an image from text","description":"Result URLs are temporary (~24h) — download what you need. Pass `async: true` or a `webhook_url` to get an `img_` task id back immediately instead of hanging on the poll.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationRequest"},"example":{"model":"nano-banana","prompt":"A red bicycle on a wet street"}}}},"responses":{"200":{"description":"Generated image(s).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResponse"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Upstream did not finish within the 540s poll budget.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/images/edits":{"post":{"tags":["Images"],"operationId":"editImage","summary":"Edit an image (image-to-image)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageEditRequest"},"example":{"model":"nano-banana-edit","prompt":"Make the sky overcast","image":"https://files.kunavo.com/example.png"}}}},"responses":{"200":{"description":"Edited image(s).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResponse"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/images/{id}":{"get":{"tags":["Images"],"operationId":"getImageTask","summary":"Poll an asynchronous image task","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"An `img_` task id."}],"responses":{"200":{"description":"Task status: queued | in_progress | completed | failed.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/video/generations":{"post":{"tags":["Video"],"operationId":"createVideoSync","summary":"Generate a video (synchronous)","description":"The caller hangs while the server polls upstream, up to 540s. Generation takes 30s to several minutes — set your HTTP client timeout to 600s. Prefer the asynchronous pair below in production.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoRequest"},"example":{"model":"veo-3","prompt":"A drone shot over a coastline","duration":5}}}},"responses":{"200":{"description":"A permanent files.kunavo.com URL.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}}}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Upstream did not finish within the 540s poll budget.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/videos":{"post":{"tags":["Video"],"operationId":"createVideo","summary":"Submit a video generation task (asynchronous)","description":"OpenAI Sora-shaped. Returns in seconds; poll `GET /videos/{id}` until the clip is ready. Recommended polling: 5s with exponential backoff up to 30s. Results stay queryable for ~30 days.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":128},"description":"Same key within ~24h replays the original task instead of submitting again."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoRequest"}}}},"responses":{"200":{"description":"Idempotent replay of an existing task.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoTask"}}}},"202":{"description":"Task accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoTask"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/videos/{id}":{"get":{"tags":["Video"],"operationId":"getVideo","summary":"Poll a video generation task","description":"Owner-scoped: an id belonging to another account returns 404, indistinguishable from one that was never issued.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"A `vid_` task id."}],"responses":{"200":{"description":"Current task state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoTask"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/embeddings":{"post":{"tags":["Embeddings"],"operationId":"createEmbedding","summary":"Create embeddings","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","input"],"properties":{"model":{"type":"string"},"input":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"encoding_format":{"type":"string","enum":["float","base64"]},"dimensions":{"type":"integer"}}},"example":{"model":"text-embedding-3-small","input":"The quick brown fox"}}}},"responses":{"200":{"description":"Standard OpenAI embeddings response.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audio/speech":{"post":{"tags":["Audio"],"operationId":"createSpeech","summary":"Synthesize speech from text","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","input"],"properties":{"model":{"type":"string"},"input":{"description":"Text to synthesize, or an object for advanced models."},"voice":{"type":"string"},"response_format":{"type":"string"},"speed":{"type":"number"}}},"example":{"model":"tts-1","input":"Hello there","voice":"alloy"}}}},"responses":{"200":{"description":"Audio bytes.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audio/transcriptions":{"post":{"tags":["Audio"],"operationId":"createTranscription","summary":"Transcribe audio to text","description":"Available whenever /models lists a model with `kunavo.endpoint` of `stt`.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"model":{"type":"string"},"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Transcription result.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audio/music":{"post":{"tags":["Audio"],"operationId":"createMusic","summary":"Generate music","description":"Available whenever /models lists a model with `kunavo.endpoint` of `audio_music`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model"],"properties":{"model":{"type":"string"},"prompt":{"type":"string"}},"additionalProperties":true}}}},"responses":{"200":{"description":"Generated track(s).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audio/music/jobs":{"post":{"tags":["Audio"],"operationId":"createMusicTask","summary":"Generate music asynchronously","description":"The task form of /audio/music. A track takes minutes, which is longer than most HTTP clients will wait, so this returns immediately and you poll /audio/music/jobs/{id} — or supply an https `webhook_url` and be told when it settles.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MusicRequest"}}}},"responses":{"200":{"description":"The task, at status `queued`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaTask"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audio/music/jobs/{id}":{"get":{"tags":["Audio"],"operationId":"getMusicTask","summary":"Poll a music task","description":"Only visible to the account that created it. Poll every few seconds with backoff; `status` reaching `completed` or `failed` is terminal.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The task id returned when it was created."}],"responses":{"200":{"description":"The task.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaTask"}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/files":{"post":{"tags":["Files"],"operationId":"uploadFile","summary":"Upload a source image","description":"Image-edit and image-to-video need a source the model can fetch. Upload multipart/form-data with a `file` field (or JSON `{\"file\":\"data:...\"}`) and reuse the returned permanent URL. Max 25 MB. You can also inline a `data:` URI directly in `image` / `image_url` and Kunavo hosts it automatically.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"A permanent files.kunavo.com URL.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/usage":{"get":{"tags":["Usage"],"operationId":"getUsage","summary":"Your own usage totals","description":"Requests, errors, input/output/cached tokens and cost for the calling key's owner — the same numbers the dashboard shows, so a customer can reconcile a bill without screen-scraping. Bucketed by day or hour, optionally split by model.","parameters":[{"name":"start_date","in":"query","schema":{"type":"string","format":"date"},"description":"UTC calendar date, YYYY-MM-DD."},{"name":"end_date","in":"query","schema":{"type":"string","format":"date"},"description":"UTC calendar date, YYYY-MM-DD. On or after start_date, and not in the future."},{"name":"bucket","in":"query","schema":{"type":"string","enum":["day","hour"],"default":"day"},"description":"Bucket size. The maximum span depends on the bucket."},{"name":"group_by","in":"query","schema":{"type":"string","enum":["model"]},"description":"Comma-separated. Only `model` is supported today."}],"responses":{"200":{"description":"Usage rows for the window.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"invalid_request_error — malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"authentication_error — missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_quota — wallet empty or spend cap hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"model_not_found — unknown or disabled model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Gateway error. Retry with exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A Kunavo API key. Keys are prefixed `sk-kn-`."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key","description":"The same key in the header the Anthropic SDKs send. Accepted on every endpoint."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI-shaped error class, e.g. invalid_request_error, authentication_error, insufficient_quota."},"code":{"type":["string","null"]},"param":{"type":["string","null"]}}}}},"Model":{"type":"object","properties":{"id":{"type":"string","description":"The slug to pass as `model`."},"object":{"type":"string","const":"model"},"created":{"type":"integer","description":"Release date as a Unix timestamp; falls back to catalog listing date."},"owned_by":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"context_length":{"type":"integer","description":"Context window in tokens (OpenRouter's spelling of the field). Chat models only — absent on image, video and audio models. Anthropic and OpenAI count generated output inside this figure; Google publishes an input limit with the output allowance on top. Each vendor's own convention, unaltered."},"kunavo":{"type":"object","properties":{"category":{"type":"string"},"endpoint":{"type":"string","enum":["chat","image","image_edit","video","tts","stt","embedding","audio_music"],"description":"Which HTTP endpoint to POST to for this model."},"capabilities":{"type":"array","items":{"type":"string"}},"released_at":{"type":["string","null"],"format":"date"},"context_window":{"type":["integer","null"],"description":"Same value as `context_length`, null rather than absent on non-chat models."},"max_output_tokens":{"type":["integer","null"],"description":"Cap on one response, in tokens. Null on non-chat models."},"billing":{"type":"object","additionalProperties":true}}}}},"ChatCompletionRequest":{"type":"object","required":["model","messages"],"properties":{"model":{"type":"string","description":"A chat model slug from /models."},"messages":{"type":"array","items":{"type":"object","additionalProperties":true}},"temperature":{"type":"number","minimum":0,"maximum":2},"top_p":{"type":"number","minimum":0,"maximum":1},"max_tokens":{"type":"integer","description":"Output cap. Reasoning tokens are counted separately."},"stream":{"type":"boolean"},"tools":{"type":"array","items":{"type":"object","additionalProperties":true}},"tool_choice":{"description":"\"auto\" | \"none\" | a named tool."},"response_format":{"type":"object","additionalProperties":true},"seed":{"type":"integer"},"stop":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}}},"ChatCompletion":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","const":"chat.completion"},"created":{"type":"integer"},"model":{"type":"string"},"choices":{"type":"array","items":{"type":"object","additionalProperties":true}},"usage":{"type":"object","properties":{"prompt_tokens":{"type":"integer"},"completion_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"completion_tokens_details":{"type":"object","properties":{"reasoning_tokens":{"type":"integer"}}}}}}},"ImageGenerationRequest":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string"},"prompt":{"type":"string"},"size":{"type":"string","description":"OpenAI-style \"1024x1024\"; mapped to aspect ratio."},"resolution":{"type":"string","description":"Tier for tiered models, e.g. \"1K\" / \"2K\" / \"4K\"."},"quality":{"type":"string","enum":["standard","hd"]},"n":{"type":"integer","description":"Most models return 1 regardless."},"async":{"type":"boolean"},"webhook_url":{"type":"string","format":"uri"},"input":{"type":"object","additionalProperties":true,"description":"Escape hatch: raw upstream params, bypasses the adapter."}}},"ImageEditRequest":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string"},"prompt":{"type":"string"},"image":{"type":"string","description":"Source image: an https URL or a data: base64 URI."},"image_urls":{"type":"array","items":{"type":"string"},"description":"Multiple reference images, where supported."},"resolution":{"type":"string"},"async":{"type":"boolean"},"webhook_url":{"type":"string","format":"uri"},"input":{"type":"object","additionalProperties":true}}},"ImageResponse":{"type":"object","properties":{"created":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}}},"VideoRequest":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string"},"prompt":{"type":"string","description":"Description of the shot."},"image_url":{"type":"string","description":"Source image for image-to-video (https or data: URI)."},"image_urls":{"type":"array","items":{"type":"string"},"description":"Multiple images; takes precedence over image_url."},"image_mode":{"type":"string","enum":["frame","reference"],"description":"\"frame\" (default: 1 image = first frame, 2 images = first + last frame) or \"reference\" (up to 3 style references)."},"duration":{"type":"integer","description":"Clip length in seconds, model-dependent (often 5–10)."},"aspect_ratio":{"type":"string","examples":["16:9","9:16","1:1"]},"resolution":{"type":"string","examples":["720p","1080p","4k"]},"input":{"type":"object","additionalProperties":true}}},"MusicRequest":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string","description":"A model whose `kunavo.endpoint` is `audio_music`."},"prompt":{"type":"string","description":"In custom mode this is the lyrics; otherwise a description of the track."},"customMode":{"type":"boolean","description":"Treat `prompt` as lyrics and take the arrangement from `style`."},"instrumental":{"type":"boolean"},"style":{"type":"string"},"title":{"type":"string"},"webhook_url":{"type":"string","format":"uri","description":"https only. The task is POSTed here when it settles."}}},"MediaTask":{"type":"object","description":"The shared shape of an asynchronous image, video or music task.","properties":{"id":{"type":"string"},"object":{"type":"string","description":"`image`, `video` or `music`."},"status":{"type":"string","enum":["queued","in_progress","completed","failed"]},"model":{"type":"string"},"created_at":{"type":"integer"},"completed_at":{"type":["integer","null"]},"expires_at":{"type":["integer","null"]},"progress":{"type":"integer","description":"0 while queued or in progress, 100 once terminal."},"output":{"type":["object","null"],"properties":{"url":{"type":"string","format":"uri"},"urls":{"type":"array","items":{"type":"string","format":"uri"}},"archived":{"type":"boolean","description":"true when the URLs point at permanent storage. false means the archive step failed and these are upstream temporaries that stop resolving after roughly 24 hours — re-host if you need the asset long-term."}}},"error":{"type":["object","null"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}},"stalled":{"type":"boolean","description":"true when the task has been non-terminal for longer than expected. It is a hint that the poller is not advancing it, not that the model is still working."}}},"VideoTask":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","const":"video"},"status":{"type":"string","enum":["queued","in_progress","completed","failed"]},"model":{"type":"string"},"created_at":{"type":"integer"},"completed_at":{"type":["integer","null"]},"expires_at":{"type":"integer"},"progress":{"type":"integer"},"output":{"type":["object","null"],"properties":{"url":{"type":"string","format":"uri"},"urls":{"type":"array","items":{"type":"string","format":"uri"}},"duration_seconds":{"type":"number"},"resolution":{"type":"string"}}},"error":{"type":["object","null"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}