{"openapi":"3.1.0","info":{"title":"Kubeez Public REST API","summary":"AI media, music, dialogue, and ads — programmatic access with API keys.","description":"## Getting started\n\n1. Create an API key in Kubeez (Settings → API Keys). Keys must start with `sk_live_`.\n2. Call **`GET /v1/models`** and copy an enabled **`model_id`**.\n3. Start a job (`POST /v1/generate/media`, `/generate/music`, or `/generate/dialogue`).\n4. Await completion by polling the matching **`GET .../{id}`** status (REST API does not offer long-lived SSE for job status; use the [MCP server](https://mcp.kubeez.com) for tool-based status checks).\n\n## Authentication\n\nSend either:\n- Header **`X-API-Key: sk_live_...`**, or\n- Header **`Authorization: Bearer sk_live_...`**\n\n## Scopes\n\nEach endpoint requires appropriate key scopes (e.g. `generate:media`, `generate:music`). Missing scope returns **403**.\n\n## Errors\n\nValidation and business errors typically return JSON with **`error`** and **`message`**. Rate limits return **429** with **`rate_limit_exceeded`**.\n\n## OpenAPI\n\nUse **Try it out** with your key. Prefer **operation ids** (e.g. `v1_media_generate`) when discussing integrations.","version":"1.0.0"},"paths":{"/v1/models":{"get":{"tags":["Models"],"summary":"List models","description":"**When to use:** before any `generate/*` call. Returns enabled models from `ai_models_config` with `generation_types`, input requirements, credits, and limits. **Common mistakes:** using an internal or disabled model id—always copy **`model_id`** from this response into generate requests.\n\nOptional `model_type` filter: `image`, `video`, `music`, `speech`.\n\n**Scope:** `read:generations`.","operationId":"v1_models_list","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"model_type","in":"query","required":false,"schema":{"type":"string","description":"Filter by model type (image, video, music, speech)","default":"","title":"Model Type"},"description":"Filter by model type (image, video, music, speech)"}],"responses":{"200":{"description":"Enabled models and metadata for API clients.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelsListResponse"},"examples":{"catalog":{"summary":"Typical catalog fragment","value":{"models":[{"model_id":"nano-banana-2","display_name":"Nano Banana 2","model_type":"image","provider":"fal","cost_per_generation":8,"estimated_time_seconds":12,"generation_types":["text-to-image","image-to-image"],"input_media_types":["image"],"requires_input_media":false,"capabilities":{"prompt_max_chars":5000,"duration_options":[],"max_input_images":8,"supports_negative_prompt":false,"supports_sound":false},"usage_notes":"TIER: Default image model. Best value — similar quality to Pro at half the cost. Great for general text-to-image and image editing. Text-to-image: omit source_media_urls; Image-to-image: pass 1-8 images in source_media_urls."},{"model_id":"seedance-2","display_name":"Seedance 2","model_type":"video","provider":"bytedance","cost_per_generation":128,"estimated_time_seconds":180,"generation_types":["text-to-video","image-to-video"],"input_media_types":["image","video","audio"],"requires_input_media":false,"capabilities":{"prompt_max_chars":2500,"duration_options":["4s","5s","6s","7s","8s","9s","10s","11s","12s","13s","14s","15s"],"max_input_images":9,"max_input_videos":3,"max_input_audios":3,"supports_negative_prompt":false,"supports_sound":true,"video_audio":"toggle_via_sound_param"},"usage_notes":"TIER: Seedance 2 — STANDARD quality tier (higher fidelity than seedance-2-fast). Multimodal reference: up to 9 images + 3 videos + 3 audio clips. Duration 4-15s, 480p/720p. Rate: 16 cr/s 480p, 38 cr/s 720p; with video ref 13/29 cr/s.","cost_note":"Per-second pricing — actual cost = duration_seconds × base_credits_per_second. With a reference video, billing assumes the worst case (15s) for the reference."}],"total_count":2}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"additionalProperties":true,"properties":{"error":{"description":"Machine-readable error code.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional hint for fixing the request.","title":"Hint"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Max requests allowed in the window.","title":"Limit"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Window size in seconds.","title":"Reset After"}},"required":["error"],"title":"RateLimitBody","type":"object"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/generate/media/extend":{"post":{"tags":["Media"],"summary":"Extend a Veo 3.1 video","description":"**When to use:** lengthen a completed Veo 3.1 video by appending a new AI-generated clip. **Scope:** `generate:media`.\n\n**Source constraints** (enforced server-side): the source generation must be (1) yours, (2) a Veo 3.1 generation (not other video models), (3) not itself an extend, and (4) not a 1080p output.\n\n**Pricing:** flat per tier — `lite`=45, `fast`=75, `quality`=300 credits. No per-duration billing, no `estimate_generation_cost` round-trip needed.\n\n**Flow:** (1) `GET /v1/generate/media/{id}` on your Veo 3.1 generation — note `source_task_id`; (2) call this endpoint with it; (3) poll `GET /v1/generate/media/{new_id}` until `completed`.","operationId":"v1_media_extend","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VeoExtendRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationAccepted"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/generate/media":{"post":{"tags":["Media"],"summary":"Start media generation","description":"**When to use:** create an image or video job. **Scope:** `generate:media`.\n\n**Returns:** `generation_id` (UUID) — use it as `{id}` in `GET /v1/generate/media/{id}`.\n\n**Flow:** (1) optional `POST /v1/upload/media` for local files → `source_media_urls`; (2) this endpoint; (3) poll `GET /v1/generate/media/{id}` until `status` is `completed` or `failed`.\n\n**Common mistakes:** wrong `generation_type` for the model; omitting `source_media_urls` when the model requires input; using a music or speech model here (use music/dialogue endpoints instead).\n\n---\n\n### Model capability summary\n\nCondensed view of the most-used models. The authoritative, always-up-to-date list — including the remaining tiers and full per-model `capabilities` — is at **`GET /v1/models`**.\n\n| Model | Types | Resolutions / Duration | Max inputs | Sound | Notes |\n|---|---|---|---|---|---|\n| **`nano-banana-2`** | text-to-image, image-to-image | — | 8 img | — | Best value |\n| **`nano-banana-2-lite`** | text-to-image, image-to-image | — | 10 img | — | Lite sibling of nano-banana-2 — single tier (no resolution options), the fastest and most affordable Nano B... |\n| **`nano-banana-pro`** | text-to-image, image-to-image | — | 8 img | — | Reserve for complex multi-element compositions, intricate typography, print-quality brand assets |\n| **`z-image`** | text-to-image | — | — | — | Use when speed matters more than detail — quick previews, rapid iteration, bulk runs |\n| **`z-image-hd`** | text-to-image | — | — | — | HD variant of Z-Image — higher resolution (1408px base) at same speed |\n| **`logo-maker`** | text-to-image | — | — | — | Recraft SVG vector logo generator — use only for brand logos / SVG vector output, not general imagery |\n| **`flux-2`** | text-to-image | — | — | — | Use when you need identity preservation across a series |\n| **`seedance-2`** | text-to-video, image-to-video | 4s, 5s, 6s, 7s, 8s, 9s, 10s, 11s, 12s, 13s, 14s, 15s | 9 img / 3 vid / 3 aud | toggle | This is the higher-quality, slower, more expensive variant of Seedance 2 |\n| **`seedance-2-fast`** | text-to-video, image-to-video | 4s, 5s, 6s, 7s, 8s, 9s, 10s, 11s, 12s, 13s, 14s, 15s | 9 img / 3 vid / 3 aud | toggle | This is the cheaper, faster variant of Seedance 2 |\n| **`seedance-2-mini`** | text-to-video, image-to-video | 4s, 5s, 6s, 7s, 8s, 9s, 10s, 11s, 12s, 13s, 14s, 15s | 9 img / 3 vid / 3 aud | toggle | This is the CHEAPEST variant of Seedance 2 — a separate model with the same multimodal capabilities as `see... |\n| **`kling-3-0-pro`** | text-to-video, image-to-video | 3s, 4s, 5s, 6s, 7s, 8s, 9s, 10s, 11s, 12s, 13s, 14s, 15s | 2 img | toggle | Kling 3 |\n| **`kling-3-0-4k`** | text-to-video, image-to-video | 3s, 4s, 5s, 6s, 7s, 8s, 9s, 10s, 11s, 12s, 13s, 14s, 15s | 2 img | toggle | Kling 3 |\n| **`kling-3-0-std`** | text-to-video, image-to-video | 3s, 4s, 5s, 6s, 7s, 8s, 9s, 10s, 11s, 12s, 13s, 14s, 15s | 2 img | toggle | Kling 3 |\n| **`kling-2-6-text-to-video`** | text-to-video | 5s, 10s | — | toggle | Superior camera motion, native audio-video sync in one pass |\n| **`kling-2-5-image-to-video-pro`** | image-to-video | — | 2 img | always | Use when you have both plates |\n| **`seedance-1-5-pro`** | text-to-video, image-to-video | 4s, 8s, 12s | 2 img | toggle | Video generation with start/end frame support |\n| **`veo3-1`** | text-to-video, first-and-last-frames, reference-to-video | 8s | 3 img | always | Premium video model |\n\n**Legend:** *Sound* — `toggle` means pass `sound: true/false`; `always` means audio is included automatically; `silent` means the model never produces audio; `—` means not applicable. *Max inputs* — caps on `source_media_urls` per media type (image/video/audio).\n\n### Quick decision rules\n- Best default image (text-to-image or editing) → **`nano-banana-2`**\n- Cheapest / fastest image → **`z-image`** (HD variant: **`z-image-hd`**)\n- Best image quality → **`nano-banana-pro`** (use `quality: 4K` for maximum)\n- Best video quality → **`kling-3-0-pro`** (or **`kling-3-0-4k`** for 4K resolution with bundled audio) or **`veo3-1`**\n- Best video value with multimodal reference → **`seedance-2`** (cheaper: **`seedance-2-fast`**, cheapest: **`seedance-2-mini`**)\n- Start + end frame image-to-video → **`kling-2-5-image-to-video-pro`**","operationId":"v1_media_generate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaGenerateRequest"}}},"required":true},"responses":{"200":{"description":"Job created. Use `generation_id` with `GET /v1/generate/media/{id}` until complete.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationAccepted"},"examples":{"started":{"summary":"Queued","value":{"generation_id":"550e8400-e29b-41d4-a716-446655440000","status":"pending","model":"example-model-id","estimated_cost_credits":12,"billing_mode":"prepaid","message":"Generation queued. Use get_generation_status to check progress."}}}}}},"400":{"description":"Invalid parameters, disabled model, family name without variant, insufficient credits, or provider failure.","content":{"application/json":{"examples":{"invalid_model":{"summary":"Unknown model","value":{"error":"invalid_model","message":"Model 'foo' is not valid for this generation type."}},"variant_required":{"summary":"Family name, not a concrete model_id","value":{"error":"variant_required","message":"'seedance-2-fast' is a model family — not a concrete model_id. Pick one of the listed variants.","family":"seedance-2-fast","available_variants":["seedance-2-fast-480p","seedance-2-fast-720p","seedance-2-fast-480p-video-ref","seedance-2-fast-720p-video-ref"],"hint":"Pull pricing / capabilities from /v1/models and re-call with a concrete variant. No credits are deducted on this error."}},"insufficient_credits":{"summary":"Insufficient credits","value":{"error":"insufficient_credits","message":"Not enough credits to start this generation."}},"missing_input":{"summary":"Missing source media","value":{"error":"missing_input","message":"This model requires source_media_urls."}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"403":{"description":"Valid API key but missing the required scope for this operation.","content":{"application/json":{"example":{"detail":"Insufficient permissions: generate:media required"},"examples":{"scope":{"summary":"Missing scope","value":{"detail":"Insufficient permissions: generate:media required"}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"properties":{"error":{"type":"string","title":"Error","description":"Machine-readable error code."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable detail."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"Optional hint for fixing the request."},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","description":"Max requests allowed in the window."},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reset After","description":"Window size in seconds."}},"additionalProperties":true,"type":"object","required":["error"],"title":"RateLimitBody"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/upload/media":{"post":{"tags":["Uploads"],"summary":"Upload input media","description":"**When to use:** you have a local/binary file and need a public URL for `source_media_urls`.\n\n**Scope:** `generate:media`. Same pipeline as the web app (`upload-media-input`).\n\n**Common mistakes:** wrong `bucket` (must be allowlisted); uploading before checking whether the target model needs image, video, or audio inputs—see `GET /v1/models`.","operationId":"v1_upload_media","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"bucket","in":"query","required":false,"schema":{"type":"string","description":"Target bucket (default media-inputs). Must match upload-media-input allowlist.","default":"media-inputs","title":"Bucket"},"description":"Target bucket (default media-inputs). Must match upload-media-input allowlist."}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_v1_upload_media"}}}},"responses":{"200":{"description":"Public URL(s) for use as `source_media_urls`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"},"examples":{"ok":{"summary":"Single file","value":{"success":true,"urls":["https://storage.example.com/media-inputs/u/abc/file.png"],"bucket":"media-inputs","uploaded":1}}}}}},"400":{"description":"Invalid bucket, rejected file, or upload failure.","content":{"application/json":{"examples":{"invalid_bucket":{"summary":"Bad bucket","value":{"error":"invalid_bucket","message":"Bucket not allowed"}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"403":{"description":"Valid API key but missing the required scope for this operation.","content":{"application/json":{"example":{"detail":"Insufficient permissions: generate:media required"},"examples":{"scope":{"summary":"Missing scope","value":{"detail":"Insufficient permissions: generate:media required"}}}}}},"413":{"description":"Uploaded file exceeds max size.","content":{"application/json":{"example":{"error":"file_too_large","message":"File too large"}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"additionalProperties":true,"properties":{"error":{"description":"Machine-readable error code.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional hint for fixing the request.","title":"Hint"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Max requests allowed in the window.","title":"Limit"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Window size in seconds.","title":"Reset After"}},"required":["error"],"title":"RateLimitBody","type":"object"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}},"503":{"description":"Upload storage not configured (self-hosted).","content":{"application/json":{"example":{"error":"upload_not_configured","message":"Upload is not configured"}}}}}}},"/v1/generate/media/{id}":{"get":{"tags":["Media"],"summary":"Media generation status","description":"**When to use:** check job state. `{id}` is the **`generation_id`** from `POST /v1/generate/media`.\n\nPoll every few seconds until `status` is `completed` and output URLs are non-null (CDN/R2), or `outputs[].optimization_status` is `failed`. While CDN upload runs, `status` may stay `processing` with null URLs.\n\n**Scope:** `read:generations`.","operationId":"v1_media_status_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Current job state and outputs (URLs may be null while CDN optimization runs).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationStatus"},"examples":{"completed":{"summary":"Completed with outputs","value":{"id":"550e8400-e29b-41d4-a716-446655440000","status":"completed","cdn_ready":true,"model":"example-model-id","prompt":"A hero shot of a mug","generation_type":"text-to-image","created_at":"2026-01-15T12:00:00Z","completed_at":"2026-01-15T12:00:45Z","processing_time_ms":42000,"credits_deducted":12,"outputs":[{"id":"out-1","media_type":"image","url":"https://cdn.example.com/image.png","optimized_url":"https://cdn.example.com/image.png","thumbnail_url":"https://cdn.example.com/thumb.png","optimization_status":"completed","width":1024,"height":1024,"format":"png","file_size":240000}]}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"404":{"description":"Generation id does not exist or is not accessible to this user.","content":{"application/json":{"examples":{"not_found":{"summary":"Not found","value":{"error":"not_found","message":"Generation not found or access denied."}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"additionalProperties":true,"properties":{"error":{"description":"Machine-readable error code.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional hint for fixing the request.","title":"Hint"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Max requests allowed in the window.","title":"Limit"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Window size in seconds.","title":"Reset After"}},"required":["error"],"title":"RateLimitBody","type":"object"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/story/generate":{"post":{"tags":["Media"],"summary":"Generate a long-form story video","description":"**When to use:** queue a storyboard (up to 12 scenes / 90 seconds total) for background generation and stitching. **Scope:** `generate:media`.\n\n**Returns:** `story_job_id` — poll `GET /v1/story/{story_job_id}` for progress.\n\n**Flow:** scenes are billed individually as each one starts; the final stitched video lands in the gallery once every scene completes.","operationId":"v1_story_generate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryGenerateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryJobAccepted"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/story/{job_id}":{"get":{"tags":["Media"],"summary":"Story video job status","description":"**When to use:** check story video job state. `{job_id}` is the **`story_job_id`** from `POST /v1/story/generate`. **Scope:** `generate:media`.\n\nReturns the job row plus a `scenes` array (one entry per scene, ordered by index) with each scene's `status`.","operationId":"v1_story_status_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryJobStatus"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/premium-video/orders/{order_id}":{"get":{"tags":["Media"],"summary":"Premium video order status","description":"**When to use:** poll a premium video order for its live status, the delivered MP4 URL (once ready), and any open design-pass questions the worker is waiting on.\n\n`{order_id}` is the order id returned when the order was placed. Always scoped to the authenticated user's own order.\n\n**Questions:** when `open_questions` is non-empty the worker is blocked on the customer — relay each question (with its options) and submit the reply via `POST /v1/premium-video/questions/{question_id}/answer` PROMPTLY; each has a short answer window (see `window_expires_at`) after which the worker autopilots.\n\n**Scope:** `read:generations`.","operationId":"v1_premium_video_order_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PremiumVideoOrderStatus"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/premium-video/questions/{question_id}/answer":{"post":{"tags":["Media"],"summary":"Answer a premium video design question","description":"**When to use:** submit the customer's answer to an open design-pass question surfaced by `GET /v1/premium-video/orders/{order_id}`. Scoped to the caller's own order.\n\nA question can be answered only once (a second attempt returns `already_answered`). Late answers past the window are still accepted — the worker may have autopiloted already.\n\n**Scope:** `generate:media`.","operationId":"v1_premium_video_answer_question","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"question_id","in":"path","required":true,"schema":{"type":"string","title":"Question Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnswerQuestionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PremiumVideoAnswerAccepted"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/generate/music":{"post":{"tags":["Music"],"summary":"Start music generation","description":"**When to use:** create a music job (not dialogue/TTS). **Scope:** `generate:music`.\n\n**Returns:** `generation_id` — use as `{id}` in `GET /v1/generate/music/{id}`.\n\n**Modes:** *Simple* — non-empty `prompt`. *Advanced* — non-empty `title` **and** `style`, `vocal_gender` when not instrumental, and **exactly one** of `song_description` or `lyrics`.\n\n**Common mistakes:** mixing simple and advanced fields; sending both `song_description` and `lyrics`.","operationId":"v1_music_generate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MusicGenerateRequest"}}},"required":true},"responses":{"200":{"description":"Music job started. Poll `GET /v1/generate/music/{id}` using `generation_id`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MusicGenerationAccepted"},"examples":{"started":{"summary":"Pending","value":{"generation_id":"660e8400-e29b-41d4-a716-446655440001","status":"pending","estimated_time_seconds":120,"billing_mode":"prepaid","message":"Music generation started. Use get_music_status to check progress."}}}}}},"400":{"description":"Invalid music mode fields, disabled model, or insufficient credits.","content":{"application/json":{"examples":{"invalid_input":{"summary":"Invalid input","value":{"error":"invalid_input","message":"Provide prompt (simple mode), or title+style+vocal_gender and exactly one of song_description or lyrics (advanced mode)."}},"model_disabled":{"summary":"Model not available","value":{"error":"model_disabled","message":"Music model 'X' is not available or is disabled.","hint":"Valid model values: V4, V4_5, V4_5PLUS, V5, V5_5."}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"403":{"description":"Valid API key but missing the required scope for this operation.","content":{"application/json":{"example":{"detail":"Insufficient permissions: generate:media required"},"examples":{"scope":{"summary":"Missing scope","value":{"detail":"Insufficient permissions: generate:media required"}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"properties":{"error":{"type":"string","title":"Error","description":"Machine-readable error code."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable detail."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"Optional hint for fixing the request."},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","description":"Max requests allowed in the window."},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reset After","description":"Window size in seconds."}},"additionalProperties":true,"type":"object","required":["error"],"title":"RateLimitBody"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/generate/music/{id}":{"get":{"tags":["Music"],"summary":"Music generation status","description":"**When to use:** polling music completion. `{id}` is **`generation_id`** from `POST /v1/generate/music`.\n\nSongs include `audio_url`, `stream_url` / `stream_audio_url` (canonical CDN when mirrored).\n\nPoll every few seconds until `status` is `completed` or `failed`.\n\n**Scope:** `read:generations`.","operationId":"v1_music_status_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Music job state and song records when available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MusicGenerationStatus"},"examples":{"completed":{"summary":"Done","value":{"id":1,"status":"completed","progress":100,"prompt":"Upbeat electronic","style":"EDM","title":"Night Run","created_at":"2026-01-15T12:00:00Z","credits_deducted":20,"songs":[{"id":101,"title":"Night Run","audio_url":"https://cdn.example.com/song.mp3","stream_url":"https://cdn.example.com/song-stream.mp3","stream_audio_url":"https://cdn.example.com/song-stream.mp3","cover_image_url":"https://cdn.example.com/cover.jpg","duration_seconds":180,"lyrics":"[Verse] ...","style":"EDM"}]}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"404":{"description":"Generation id does not exist or is not accessible to this user.","content":{"application/json":{"examples":{"not_found":{"summary":"Not found","value":{"error":"not_found","message":"Generation not found or access denied."}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"additionalProperties":true,"properties":{"error":{"description":"Machine-readable error code.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional hint for fixing the request.","title":"Hint"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Max requests allowed in the window.","title":"Limit"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Window size in seconds.","title":"Reset After"}},"required":["error"],"title":"RateLimitBody","type":"object"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/generate/dialogue":{"post":{"tags":["Dialogue"],"summary":"Text-to-speech (single voice)","description":"**When to use:** single-voice TTS (not music — use `/v1/generate/music` for songs). **Scopes:** `generate:speech` or `generate:music`.\n\n**Tracking:** use **`generation_id`** from the response with **Media** `GET /v1/generate/media/{id}` — never `GET /v1/generate/music/...`.\n\n**Common mistakes:** sending music-style `prompt`/`title` here; expecting `[laughs]` / `[whispers]` audio tags to be interpreted (they are stripped).","operationId":"v1_dialogue_generate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DialogueGenerateRequest"}}},"required":true},"responses":{"200":{"description":"Dialogue job started (same tracking pattern as media — use **Media** `GET /v1/generate/media/{id}`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DialogueGenerationAccepted"},"examples":{"started":{"summary":"Queued","value":{"generation_id":"550e8400-e29b-41d4-a716-446655440000","status":"pending","model":"example-model-id","estimated_cost_credits":12,"billing_mode":"prepaid","message":"Generation queued. Use get_generation_status to check progress."}}}}}},"400":{"description":"Invalid dialogue payload, model, or credits.","content":{"application/json":{"examples":{"invalid":{"summary":"Invalid dialogue","value":{"error":"invalid_input","message":"dialogue must be a non-empty list"}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"403":{"description":"Valid API key but missing the required scope for this operation.","content":{"application/json":{"example":{"detail":"Insufficient permissions: generate:media required"},"examples":{"scope":{"summary":"Missing scope","value":{"detail":"Insufficient permissions: generate:media required"}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"properties":{"error":{"type":"string","title":"Error","description":"Machine-readable error code."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable detail."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"Optional hint for fixing the request."},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","description":"Max requests allowed in the window."},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reset After","description":"Window size in seconds."}},"additionalProperties":true,"type":"object","required":["error"],"title":"RateLimitBody"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/generate/separation":{"post":{"tags":["Media"],"summary":"Separate audio into stems","description":"**When to use:** split audio into vocals and instrumental tracks. **Scope:** `generate:media`.\n\n**Flow:** (1) upload audio via `POST /v1/upload/media`; (2) call this endpoint; (3) poll `GET /v1/generate/separation/{separation_id}` until completed — results include `vocals_url` and `instrumental_url`.\n\n**Note:** for a full workflow with preview, download, and library management, use the Kubeez web app at [kubeez.com/audio/separation](https://kubeez.com/audio/separation).","operationId":"v1_separation_generate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeparationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeparationAccepted"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/generate/remove-background":{"post":{"tags":["Media"],"summary":"Remove an image background","description":"**When to use:** you need the subject cut out of an image on a real transparent background (alpha) — logos, product shots, mascots, anything you will composite. Prompting an image model for a 'transparent background' bakes a white or checkerboard fill into the pixels instead; use this. **Scope:** `generate:media`.\n\nTakes no prompt — this is an operation on an existing image, not a `generate_media` model.\n\n**Flow:** (1) upload the image via `POST /v1/upload/media` (or reuse a generation output URL); (2) call this endpoint; (3) poll `GET /v1/generate/media/{generation_id}` until completed — the output is a PNG with a transparent background.\n\n**Billing:** flat per image. **Input:** one .jpg / .jpeg / .png / .webp, max 5MB.","operationId":"v1_remove_background","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveBackgroundRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationAccepted"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/generate/separation/{id}":{"get":{"tags":["Media"],"summary":"Separation status","description":"Poll until status is `completed`. Results include `vocals_url` and `instrumental_url`.","operationId":"v1_separation_status","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeparationStatus"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/generate/captions":{"post":{"tags":["Media"],"summary":"Caption a video","description":"**When to use:** feed a video, get back a finished MP4 with word-timed captions burned in. Captions are styled automatically and placed to avoid faces, on-screen text, and the main subject. **Scope:** `generate:media`.\n\n**Flow:** (1) upload video via `POST /v1/upload/media`; (2) call this endpoint with the video URL; (3) response returns `generation_id` immediately (async); (4) poll `GET /v1/generate/media/{id}` until the captioned video appears in outputs.\n\n**Billing:** transcription plus a per-second render fee. The render cost is checked before transcription runs, so an unaffordable job never bills a partial step. Max 30 minutes per video.","operationId":"v1_captions_generate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptionGenerateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptionsAccepted"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/balance":{"get":{"tags":["Account"],"summary":"Account credit balance","description":"**When to use:** check prepaid credits before large batch jobs.\n\nReturns balance and purchase URL. Active team members read the shared team wallet and get an extra `team` block (role, per-member monthly limit, cycle usage, remaining headroom). **Common mistakes:** confusing credits with per-model `base_credits` in `/models`.\n\n**Scope:** `read:balance`.","operationId":"v1_balance_get","responses":{"200":{"description":"Current prepaid credit balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceResponse"},"examples":{"ok":{"summary":"Balance","value":{"credits":12500,"message":"You have 12,500 credits available.","purchase_url":"https://kubeez.com/billing"}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"properties":{"error":{"type":"string","title":"Error","description":"Machine-readable error code."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable detail."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"Optional hint for fixing the request."},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","description":"Max requests allowed in the window."},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reset After","description":"Window size in seconds."}},"additionalProperties":true,"type":"object","required":["error"],"title":"RateLimitBody"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/generations":{"get":{"tags":["Account"],"summary":"List generations (history)","description":"**When to use:** audit or debug past jobs for the authenticated user.\n\nOptional filters: `status`, `model`, `generation_type`. **Common mistakes:** expecting another user's jobs (always scoped to the API key owner).\n\n**Scope:** `read:generations`.","operationId":"v1_generations_list","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","description":"Filter by job status (e.g. completed, processing).","default":"","title":"Status"},"description":"Filter by job status (e.g. completed, processing)."},{"name":"model","in":"query","required":false,"schema":{"type":"string","description":"Filter by model id.","default":"","title":"Model"},"description":"Filter by model id."},{"name":"generation_type","in":"query","required":false,"schema":{"type":"string","description":"Filter by generation type.","default":"","title":"Generation Type"},"description":"Filter by generation type."}],"responses":{"200":{"description":"Filtered list of recent generations for this user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationsListResponse"},"examples":{"list":{"summary":"History fragment","value":{"generations":[],"count":0,"limit":20}}}}}},"401":{"description":"Missing or invalid API key (`sk_live_...`). Use `X-API-Key` or `Authorization: Bearer`.","content":{"application/json":{"example":{"detail":"API key missing"},"examples":{"missing":{"summary":"No key","value":{"detail":"API key missing"}},"invalid":{"summary":"Invalid / revoked","value":{"detail":"Invalid or revoked API key"}}}}}},"429":{"description":"Too many requests for this tool bucket.","content":{"application/json":{"schema":{"additionalProperties":true,"properties":{"error":{"description":"Machine-readable error code.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional hint for fixing the request.","title":"Hint"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Max requests allowed in the window.","title":"Limit"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Window size in seconds.","title":"Reset After"}},"required":["error"],"title":"RateLimitBody","type":"object"},"examples":{"rate_limit":{"summary":"Rate limit exceeded","value":{"error":"rate_limit_exceeded","message":"Rate limit exceeded for generate_media. Limit: 30 requests per 60s.","limit":30,"remaining":0,"reset_after":60}}}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/v1/assets":{"get":{"tags":["Assets"],"summary":"List the user's Asset Library","description":"**When to use:** before asking the user for media, check whether their library already has it. Each entry returns a freshly signed CDN `url` (1 h TTL) you can pass directly to `POST /v1/generate/media` as a `source_media_urls` value.\n\n**Scope:** `generate:media`.\n\n**Returns:** `{ assets: [...], count, quota_bytes, used_bytes }`. Each asset carries `id`, `name`, `kind` (image|video|audio), `mime_type`, `size_bytes`, `width` / `height` / `duration_seconds` when known, `url`, and `url_expires_at`.\n\n**Team seats:** active team members also see their team's shared assets merged into `assets` — those rows carry `shared: true`, `owner_user_id`, and `uploaded_by` `{user_id, username}`; a slim `team` block reports the team pool's `quota_bytes` / `used_bytes`.","operationId":"v1_assets_list","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetListResponse"}}}}},"security":[{"APIKeyHeader":[]}]},"post":{"tags":["Assets"],"summary":"Save a remote URL as a named asset","description":"**When to use:** the caller has an HTTPS URL (a generation output, a CDN asset, etc.) they want to reuse later under a friendly handle.\n\nServer-side fetches the URL, validates MIME / size, enforces the per-user quota (default 50 MB), and stores the bytes in R2. **Scope:** `generate:media`.\n\n**Errors:** `quota_exceeded` (free space first), `name_taken` (pick another `name`), `unsupported_type` (re-encode), `fetch_failed` (URL not reachable), `shared_scope_required` (account has an active team and `shared` was omitted — ask the user personal vs team library, retry with `shared` set).","operationId":"v1_assets_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAssetRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetResponse"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/assets/{id}":{"patch":{"tags":["Assets"],"summary":"Rename an asset","description":"Rename an existing asset by `id`. Renaming does NOT invalidate the file or break past generations — only the user-facing handle changes. **Scope:** `generate:media`.","operationId":"v1_assets_rename","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameAssetRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetResponse"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}},"delete":{"tags":["Assets"],"summary":"Delete an asset","description":"Permanently delete the R2 blob and the DB row. Past generations that already used this asset's URL keep their outputs. **Scope:** `generate:media`.","operationId":"v1_assets_delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletedResponse"}}}},"400":{"description":"Request failed validation (schema, type, enum, length or a model validator). Returned instead of 422 — this API never emits 422. No credits are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"},"example":{"error":"invalid_request","message":"aspect_ratio: Input should be '1:1', '16:9' or '9:16'","param":"aspect_ratio","errors":[{"param":"aspect_ratio","message":"Input should be '1:1', '16:9' or '9:16'"}]}}}}}}},"/health":{"get":{"summary":"Health Check","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AddAssetRequest":{"properties":{"name":{"type":"string","title":"Name","description":"Stable handle for the asset (lowercase letters/digits/`_`/`-`, max 64 chars). Must be unique within the user's library.","examples":["tesote-logo"]},"url":{"type":"string","title":"Url","description":"Public HTTPS URL the server will fetch. Must respond with one of: image/jpeg, image/png, image/webp, video/mp4, video/quicktime, video/x-matroska, video/webm, audio/mpeg, audio/wav, audio/mp3, audio/ogg, audio/mp4, audio/m4a, audio/webm. Capped at 500 MB per file.","examples":["https://cdn.example.com/logo.png"]},"shared":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Shared","description":"Where the asset lands for accounts with an active team: `true` = the shared team library, `false` = the caller's personal library. This is the end user's choice — agents should ask, not guess. REQUIRED when the account has an active team (omitting it returns `shared_scope_required` with the `team_name`). Accounts with no team ignore it; their assets are always personal.","examples":[false]}},"type":"object","required":["name","url"],"title":"AddAssetRequest","description":"Body for `POST /v1/assets`. Server-side fetch of a remote URL into the\nuser's private R2 library under a stable, reusable handle."},"AnswerQuestionRequest":{"properties":{"answer":{"type":"string","title":"Answer","description":"The customer's answer — one of the question's options, or free text.","examples":["Warm, optimistic tone"]}},"type":"object","required":["answer"],"title":"AnswerQuestionRequest","description":"Body for `POST /v1/premium-video/questions/{question_id}/answer`."},"AssetItem":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"team_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Team Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"original_filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Filename"},"mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime Type"},"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width"},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"url_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url Expires At"},"shared":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Shared"},"owner_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner User Id"},"uploaded_by":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Uploaded By"}},"additionalProperties":true,"type":"object","title":"AssetItem","description":"One Asset Library entry (shape owned by the `asset-manager` edge function).\n\n`extra=\"allow\"` so a field this model has not caught up with is still\nreturned to the client instead of silently dropped."},"AssetListResponse":{"properties":{"assets":{"items":{"$ref":"#/components/schemas/AssetItem"},"type":"array","title":"Assets"},"count":{"type":"integer","title":"Count"},"quota_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quota Bytes"},"used_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Used Bytes"},"team":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Team"}},"type":"object","required":["assets","count"],"title":"AssetListResponse","description":"`GET /v1/assets`."},"AssetResponse":{"properties":{"asset":{"anyOf":[{"$ref":"#/components/schemas/AssetItem"},{"type":"null"}]}},"type":"object","title":"AssetResponse","description":"`POST /v1/assets`, `PATCH /v1/assets/{id}`."},"BalanceResponse":{"properties":{"credits":{"type":"integer","title":"Credits"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"purchase_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Purchase Url"},"team":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Team"}},"type":"object","required":["credits"],"title":"BalanceResponse","description":"`GET /v1/balance`."},"Body_v1_upload_media":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Image, video, or audio file for use with generate/media"}},"type":"object","required":["file"],"title":"Body_v1_upload_media"},"CaptionGenerateRequest":{"properties":{"media_url":{"type":"string","title":"Media Url","description":"Video file URL from POST /v1/upload/media.","examples":["https://storage.kubeez.com/media-inputs/video.mp4"]},"quality":{"type":"string","title":"Quality","description":"'auto' (default) = auto-detect spoken language, lower cost. 'best' = select a specific language with higher accuracy.","default":"auto","examples":["auto","best"]},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Language code for 'best' quality mode. Required when quality='best'. Ignored when quality='auto'.","examples":["en","es","fr","de","it","pt"]},"code_switching":{"type":"boolean","title":"Code Switching","description":"Detect multiple spoken languages within a single video.","default":false}},"type":"object","required":["media_url"],"title":"CaptionGenerateRequest","description":"Transcribe a video and get word-level timestamped captions."},"CaptionsAccepted":{"properties":{"generation_id":{"type":"string","title":"Generation Id","description":"Poll this id with GET /v1/generate/media/{id}."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["processing"]},"credits_charged":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Credits Charged"},"next_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Step"}},"type":"object","required":["generation_id"],"title":"CaptionsAccepted","description":"Submit acknowledgement for `POST /v1/generate/captions` (transcription + render)."},"DeletedResponse":{"properties":{"ok":{"type":"boolean","title":"Ok"}},"type":"object","required":["ok"],"title":"DeletedResponse","description":"`DELETE /v1/assets/{id}`."},"DialogueGenerateRequest":{"properties":{"provider":{"type":"string","title":"Provider","description":"TTS provider: `elevenlabs` (default, ElevenLabs v3), `google` / `gemini` (Google Gemini 3.1 Flash TTS), or `seed` (Seed 1.0 Audio by ByteDance — prompt-driven, supports image/audio references). Determines the valid `voice`/`language_code` lists and whether inline `[tags]` are performed. With `seed`, `text` is a free-form prompt (≤2048 chars), there is no `voice`, and you may pass an image (`image_url`/`image_data`) OR up to 3 `audio_references` (never both).","default":"elevenlabs","examples":["elevenlabs","google","seed"]},"text":{"type":"string","minLength":1,"title":"Text","description":"Text to convert to speech. 5-5000 chars after `[bracket]` audio tags are stripped server-side. For `provider=elevenlabs`, tags are removed and not interpreted. For `provider=google`, inline tags (`[sigh]`, `[laughing]`, …) are PRESERVED and performed.","examples":["Welcome back — ready to generate?"]},"voice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Voice","description":"Voice ID. Defaults to `Rachel` (ElevenLabs) / `Kore` (Google) when omitted. ElevenLabs: one of 26 voices; Google: one of 30 voices. Unknown voices are rejected server-side with HTTP 400 before any credits are charged. Call `GET /v1/models` and read the matching model's `voice_allowlist` (`text-to-dialogue-v3` or `text-to-dialogue-gemini`).","examples":["Rachel","Drew","Aria","Sarah","James","Jane","Mark","Roger","Hope"]},"style_prompt":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Style Prompt","description":"Google/Gemini only. Natural-language delivery direction (tone, pace, accent, emotion), e.g. 'Speak with calm, warm enthusiasm'. Defaults to 'Say the following.'. Ignored for ElevenLabs."},"stability":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Stability","description":"ElevenLabs stability dial in `[0, 1]`. Higher = more stable / monotone, lower = more expressive. Default 0.5.","default":0.5},"similarity_boost":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Similarity Boost","description":"Similarity boost in `[0, 1]`. Default 0.75 server-side."},"style":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Style","description":"Style exaggeration in `[0, 1]`. Default 0 server-side."},"speed":{"anyOf":[{"type":"number","maximum":1.2,"minimum":0.7},{"type":"null"}],"title":"Speed","description":"Playback speed in `[0.7, 1.2]`. Default 1.0 server-side."},"previous_text":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"title":"Previous Text","description":"Optional preceding text used as context. Audio tags are stripped."},"next_text":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"title":"Next Text","description":"Optional following text used as context. Audio tags are stripped."},"language_code":{"type":"string","maxLength":10,"title":"Language Code","description":"ISO language code. Must be one of 29 codes the upstream wrapper accepts (`ar, bg, cs, da, de, el, en, es, fi, fil, fr, hi, hr, id, it, ja, ko, ms, nl, pl, pt, ro, ru, sk, sv, ta, tr, uk, zh`); pass `auto` to default to `en`. Anything else is rejected with HTTP 400 before any credits are charged. Call `GET /v1/models` and read `text-to-dialogue-v3.language_code_allowlist` for the current list.","default":"en","examples":["en","es","de","fr","ja","auto"]},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Seed only (`provider='seed'`). HTTPS URL of a SINGLE reference image (JPEG/PNG/WebP, ≤10 MB) used to guide the generated voice and mood. Cannot be combined with `audio_references`.","examples":["https://storage.kubeez.com/media-inputs/ref.png"]},"image_data":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Data","description":"Seed only. Base64-encoded reference image (alternative to `image_url`). Cannot be combined with `audio_references`."},"audio_references":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Audio References","description":"Seed only. Up to 3 audio references (each clip <=30s) for voice cloning, each one of `{speaker | audio_url | audio_data}`. Tag each cloned voice IN `text` by upload order as `<<TGT_SPK1>>` / `<<TGT_SPK2>>` / `<<TGT_SPK3>>` (e.g. \"Marcus (..., the actor is <<TGT_SPK1>>) says: '...'\"); @Voice1/@Audio1/@Speaker1 are also accepted and auto-normalized. Cannot be combined with an image reference."},"audio_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Audio Config","description":"Seed only. Output audio config: `{format (mp3/wav/ogg_opus/pcm), sample_rate, speech_rate (-50..100), loudness_rate (-50..100), pitch_rate (-12..12)}`."}},"type":"object","required":["text"],"title":"DialogueGenerateRequest","description":"Single-voice text-to-speech request. Two providers via `provider`:\n`elevenlabs` (default, ElevenLabs v3) or `google` / `gemini` (Google\nGemini 3.1 Flash TTS).\n\nThis is *not* music generation — tracking is done via the **Media** status\nendpoint `GET /v1/generate/media/{id}`, not `/v1/generate/music/...`.\nThe response returns a `generation_id` you can poll; outputs land in\n`media_outputs` with `media_type=audio`.\n\n**Hard limits** (enforced server-side, rejected with HTTP 400 and **no\ncredit charge**):\n- `text` is required, 5-5000 characters after `[bracket]` audio tags are stripped\n- ElevenLabs: `voice` must be one of the 26 `elevenlabs/v3` voices; audio tags\n  (`[HEY]`, `[laughs]`, `[whispers]`) are stripped and NOT interpreted;\n  `language_code` is one of 29 ISO codes (`ar, bg, cs, da, de, el, en, es, fi,\n  fil, fr, hi, hr, id, it, ja, ko, ms, nl, pl, pt, ro, ru, sk, sv, ta, tr, uk,\n  zh`), `auto` defaults to `en`.\n- Google/Gemini: `voice` must be one of the 30 Gemini voices; inline tags\n  (`[sigh]`, `[laughing]`, `[whispering]`, `[shouting]`, `[long pause]`) are\n  PRESERVED and performed; optional `style_prompt` directs delivery;\n  `language_code` is a BCP-47 code (e.g. `en-US`, `es-ES`) or `auto`. The\n  ElevenLabs tuning params (stability/similarity_boost/style/speed/\n  previous_text/next_text) are ignored.\n\n**Billing:** per 1000 characters (decimal ≤0.3 rounds down, >0.3 rounds up;\nminimum 1 credit) — read the live rate from `GET /v1/models`.","examples":[{"language_code":"en","similarity_boost":0.75,"speed":1.0,"stability":0.5,"style":0.0,"text":"Welcome back — ready to generate?","voice":"Rachel"}]},"DialogueGenerationAccepted":{"properties":{"generation_id":{"type":"string","title":"Generation Id","description":"Poll this id with GET /v1/generate/media/{id}."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["pending"]},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"billing_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Billing Mode"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","required":["generation_id"],"title":"DialogueGenerationAccepted","description":"Async submit acknowledgement for `POST /v1/generate/dialogue`."},"GenerationAccepted":{"properties":{"generation_id":{"type":"string","title":"Generation Id","description":"Poll this id with GET /v1/generate/media/{id}."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["pending"]},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"billing_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Billing Mode"},"estimated_cost_credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Cost Credits"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"aspect_ratio_adjusted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Aspect Ratio Adjusted"},"source_task_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Task Id"}},"type":"object","required":["generation_id"],"title":"GenerationAccepted","description":"Async submit acknowledgement shared by the media-generation family:\n`POST /v1/generate/media`, `/generate/media/extend`, `/generate/remove-background`."},"GenerationStatus":{"properties":{"id":{"type":"string","title":"Id","description":"The generation_id used to look this job up."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["queued","processing","completed","failed"]},"cdn_ready":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Cdn Ready"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt"},"generation_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generation Type"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At"},"processing_time_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Processing Time Ms"},"credits_deducted":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Credits Deducted"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"outputs":{"items":{"$ref":"#/components/schemas/MediaOutputItem"},"type":"array","title":"Outputs"},"source_task_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Task Id"}},"type":"object","required":["id"],"title":"GenerationStatus","description":"`GET /v1/generate/media/{id}` — job state and outputs."},"GenerationsListResponse":{"properties":{"generations":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Generations"},"count":{"type":"integer","title":"Count"},"limit":{"type":"integer","title":"Limit"}},"type":"object","required":["generations","count","limit"],"title":"GenerationsListResponse","description":"`GET /v1/generations`."},"MediaGenerateRequest":{"properties":{"prompt":{"type":"string","maxLength":8000,"minLength":1,"title":"Prompt","description":"Text prompt describing the desired output. Per-model max length is exposed in `GET /v1/models` → `capabilities.prompt_max_chars` (typically 2000-5000 chars).","examples":["A minimal product hero shot of a matte black coffee mug on marble, soft studio light","Low-angle tracking shot through a neon-lit alley at night, rain on the ground"]},"model":{"type":"string","title":"Model","description":"Model id from `GET /v1/models`. Use the public `model_id` field from that response (not an internal name). Examples below cover the most common tiers — see the full capability table in the endpoint description for the complete list.","examples":["nano-banana-2","nano-banana-2-lite","nano-banana-pro","z-image","z-image-hd","seedance-2","seedance-2-fast","kling-3-0-pro","kling-3-0-4k","veo3-1"]},"generation_type":{"type":"string","enum":["text-to-image","image-to-image","text-to-video","image-to-video"],"title":"Generation Type","description":"Task type. Allowed values are a union across all models; each individual model accepts a subset (see `capabilities.generation_types` in `GET /v1/models`). Image-to-image / image-to-video **require** at least one URL in `source_media_urls`.","default":"text-to-image"},"negative_prompt":{"type":"string","maxLength":2000,"title":"Negative Prompt","description":"What to avoid in the output. Only a few models honor this — check `capabilities.supports_negative_prompt` in `GET /v1/models`. For models that don't support it, the field is silently ignored.","default":"","examples":["blurry, low quality, watermark, text"]},"source_media_urls":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Media Urls","description":"Publicly reachable input URLs (image / video / audio). Required for `image-to-image` and `image-to-video`; optional or ignored for text-to-image / text-to-video. Per-model caps (max images / videos / audios) live in `capabilities.max_input_images` etc. For local files call `POST /v1/upload/media` first to get a public URL. File types are auto-classified by extension: `.jpg .png .webp` = image, `.mp4 .mov .webm` = video, `.mp3 .wav .m4a` = audio.","examples":[["https://example.com/subject.png"],["https://example.com/character-ref.png","https://example.com/scene-ref.mp4"]]},"aspect_ratio":{"anyOf":[{"type":"string","enum":["1:1","4:3","3:4","16:9","9:16","21:9","2:3","3:2","adaptive","auto","5:4","4:5","1:2","2:1"]},{"type":"null"}],"title":"Aspect Ratio","description":"Output framing. **Optional** — omit it and the API sends its default of `1:1`. Gemini Omni Video specifically cannot render `1:1` (it renders only 16:9 / 9:16); for that model only, an unsupported request is adjusted and the change is reported in `aspect_ratio_adjusted`. No other model sets that field. The union above is the full list the API accepts — individual models support a subset (e.g. nano-banana-2 covers 1:1/16:9/9:16/4:3/3:4; seedance-2 adds 21:9 and `adaptive`). Pass `adaptive` to let the model choose. A value the model does not declare is rejected with `400 unsupported_aspect` and no credits are spent. See per-model limits in the endpoint description below."},"duration":{"type":"string","title":"Duration","description":"Video duration in seconds (integer, as a string). Leave empty for image models. Per-model valid values live in `capabilities.duration_options`: e.g. **Kling 3.0 std/pro = any integer 3-15** (1-second increments, not just presets), Seedance 2 = any integer 4-15, Seedance 1.5 Pro = 4/8/12. Values outside the per-model range are clamped to the nearest valid boundary at the tool layer.","default":"","examples":["","3","5","7","10","15"]},"quality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quality","description":"Tier dial for the models that expose one. Valid values are PER MODEL and authoritative in `capabilities.quality_options` from `GET /v1/models` — e.g. P-Video: `720p` (default) / `720p-draft` / `1080p` / `1080p-draft` (bare `draft` = `720p-draft`); Seedream V4 Edit: `1K`/`2K`/`4K`; Seedream V4.5 and 5-Lite: `basic`/`high`; Grok video: `480p`/`720p`. A value the model does not declare is rejected with `400 unsupported_quality`; leave `null` for models with no quality dial. Where the tier is encoded in the model id instead (`veo3-1-…-1080p`, `p-video-1080p`), pick that variant — sending `quality` there returns `400 param_ignored_use_variant` listing the ids to use.","examples":[null,"720p","1080p-draft","2K","high"]},"seed":{"type":"integer","minimum":0.0,"title":"Seed","description":"Deterministic seed when the model supports it. `0` means auto (random). Ignored by models that don't support seeding — no error is raised.","default":0,"examples":[0,42,1234567]},"sound":{"type":"boolean","title":"Sound","description":"Generated audio on video models whose `video_audio` is `toggle_via_sound_param` (Kling 2.6, Kling 3.0, Seedance 1.5 Pro, Seedance 2). Defaults to true, so audio is ON unless you pass false. Free on Seedance 2; on Kling 2.6 / Kling 3.0 / Seedance 1.5 Pro audio costs extra (the per-output price already reflects it). Ignored (no error) for models that never produce audio (`video_audio: silent`). For models whose audio is always on (`video_audio: included`), passing `false` is rejected with a 400 (audio cannot be turned off) — omit the field or pass `true`.","default":true},"fixed_lens":{"type":"boolean","title":"Fixed Lens","description":"Keep the camera/lens static (no zoom/dolly/pan). Only honored by models where `capabilities.fixed_lens` is true (e.g. Seedance 1.5 Pro).","default":false},"shots":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Shots","description":"Scene list for scene-based / storyboard video models. Array of objects, each with `Scene` (description text) and `duration` (5, 10, or 15 seconds). Total duration determines variant: <=10s, <=15s, or >15s (25s).","examples":[[{"Scene":"A cat sitting on a windowsill","duration":5},{"Scene":"The cat jumps down","duration":5}]]},"fps":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fps","description":"Frame rate for P-Video. 24 (default) or 48. Only used by P-Video.","examples":[24,48]},"prompt_upsampling":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Prompt Upsampling","description":"Enable prompt enhancement for P-Video. Default true. Set false to use your prompt verbatim without AI enhancement."},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode","description":"Grok-specific style mode. One of `fun` (playful), `normal` (default), `spicy` (edgier). Only honored by Grok video variants (`grok-text-to-video-6s`, `grok-image-to-video`). The `spicy` value auto-downgrades to `normal` for image-to-video.","examples":["normal","fun","spicy"]},"resolution":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution","description":"Output resolution for models that accept it as a separate param (e.g. Grok video accepts `480p` or `720p`). For models where resolution is encoded in the model_id suffix (Wan 2.5, Nano Banana Pro 2K/4K), pick the variant directly instead.","examples":["480p","720p"]},"character_orientation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Character Orientation","description":"Kling motion-control variants only (`kling-2-6-motion-control-*`, `kling-3-0-motion-control-*`). Controls whether the character pose is taken from the reference image (`image`) or inferred from the motion video (`video`, default). Affects the internal duration calculation at the provider.","examples":["video","image"]},"voice_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Voice Id","description":"Gemini Omni only. Built-in voice id for generated speech. Validated against the model's voice_allowlist — read it from GET /v1/models (`text-to-dialogue-gemini`). Rejected with 400 if unknown.","examples":["Kore"]},"character_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Character Ids","description":"Gemini Omni only. Character ids from your verified-face library, applied to the generated shot."},"video_trim_start_s":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Video Trim Start S","description":"Gemini Omni only. Trim the source video from this offset, in seconds.","examples":[1.5]},"video_trim_end_s":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Video Trim End S","description":"Gemini Omni only. Trim the source video at this offset, in seconds.","examples":[4.0]}},"type":"object","required":["prompt","model"],"title":"MediaGenerateRequest","description":"Start an image or video generation job.\n\nThe request shape is the same for every model — but each model accepts a\ndifferent subset of `generation_type`, `aspect_ratio`, `duration`,\n`quality`, `source_media_urls`, and sound/seed flags. **Always call\n`GET /v1/models` first** and read the `capabilities` of your chosen model:\nthe hard limits (max inputs, duration range, prompt length, supported\ntypes) are authoritative there and change as models are added or updated.","examples":[{"aspect_ratio":"1:1","duration":"","fixed_lens":false,"generation_type":"text-to-image","model":"nano-banana-2","negative_prompt":"","prompt":"A minimal product hero shot of a matte black coffee mug on marble, soft studio light","seed":0,"sound":false},{"aspect_ratio":"1:1","generation_type":"image-to-image","model":"nano-banana-2","prompt":"Change the background to a soft blue gradient; keep the subject identical","seed":0,"source_media_urls":["https://example.com/subject.png"]},{"aspect_ratio":"1:1","generation_type":"text-to-image","model":"z-image","prompt":"Sketch-style illustration of a fox in a pine forest"},{"aspect_ratio":"16:9","duration":"8","generation_type":"text-to-video","model":"seedance-2","prompt":"Low-angle tracking shot through a neon-lit alley at night, rain on the ground","sound":true},{"aspect_ratio":"9:16","duration":"6","generation_type":"text-to-video","model":"seedance-2","prompt":"Follow the subject as they walk; preserve their outfit and lighting from the reference","sound":true,"source_media_urls":["https://example.com/character-ref.png","https://example.com/scene-ref.mp4"]},{"aspect_ratio":"16:9","duration":"5","fixed_lens":false,"generation_type":"image-to-video","model":"kling-3-0-pro","prompt":"Slow dolly-in, cinematic lighting","sound":true,"source_media_urls":["https://example.com/reference-frame.png"]}]},"MediaOutputItem":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"media_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Type","examples":["image","video","audio"]},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"optimized_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Optimized Url"},"thumbnail_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail Url"},"optimization_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Optimization Status"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width"},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height"},"duration":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration"},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format"},"file_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Size"}},"type":"object","title":"MediaOutputItem","description":"One output of a media generation. URLs may be null while CDN optimization runs."},"ModelsListResponse":{"properties":{"models":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Models"},"total_count":{"type":"integer","title":"Total Count"}},"type":"object","required":["models","total_count"],"title":"ModelsListResponse","description":"`GET /v1/models`."},"MusicGenerateRequest":{"properties":{"prompt":{"type":"string","maxLength":2000,"title":"Prompt","description":"**Simple mode:** main natural-language description of the song. **Advanced mode:** leave empty — advanced mode is driven by `title`, `style`, and `song_description`/`lyrics`.","default":"","examples":["Upbeat electronic dance track with bright synths",""]},"instrumental":{"type":"boolean","title":"Instrumental","description":"Generate the track without vocals. When true, `vocal_gender`, `song_description`, and `lyrics` are ignored.","default":false},"model":{"type":"string","enum":["V4","V4_5","V4_5PLUS","V5","V5_5"],"title":"Model","description":"Music model version. `V5_5` is the current default; older versions remain available for reproducibility of older generations.","default":"V5_5"},"title":{"type":"string","maxLength":120,"title":"Title","description":"**Advanced mode only:** track title. Required alongside `style` to enter advanced mode. Leave empty in simple mode.","default":"","examples":["Midnight Drive",""]},"style":{"type":"string","maxLength":500,"title":"Style","description":"**Advanced mode only:** genre / style description (instruments, tempo, mood). Required alongside `title`.","default":"","examples":["Synthwave, 110 BPM, nostalgic neon pads",""]},"vocal_gender":{"type":"string","enum":["","m","f"],"title":"Vocal Gender","description":"**Advanced mode only, and only when `instrumental=false`:** vocal gender hint. `m` = male, `f` = female, `\"\"` = unset (default). Ignored in simple mode and when instrumental.","default":""},"song_description":{"type":"string","maxLength":2000,"title":"Song Description","description":"**Advanced mode only.** Describe what the song should be about / its vibe. Mutually exclusive with `lyrics` — pick one and leave the other empty.","default":"","examples":["A driving night-highway vibe with restrained vocals",""]},"lyrics":{"type":"string","maxLength":6000,"title":"Lyrics","description":"**Advanced mode only.** Explicit lyrics. Mutually exclusive with `song_description`. Section markers like `[Verse]`, `[Chorus]`, `[Bridge]` are supported.","default":"","examples":["","[Verse]\nDriving through the night..."]},"negative_tags":{"type":"string","maxLength":500,"title":"Negative Tags","description":"Comma-separated tags to steer the model away from (e.g. `acoustic, country`).","default":"","examples":["","acoustic, country, lo-fi"]},"style_weight":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Style Weight","description":"Advanced style adherence weight (0.0-1.0) when the provider supports it."},"weirdness_constraint":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Weirdness Constraint","description":"Creativity / weirdness dial (0.0-1.0) when the provider supports it."},"audio_weight":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Audio Weight","description":"Provider-specific audio weight (0.0-1.0) when supported."}},"type":"object","title":"MusicGenerateRequest","description":"Start a music generation job.\n\nTwo mutually-exclusive modes (server-side validated):\n\n- **Simple** — non-empty `prompt`; leave `title`, `style`, `song_description`, `lyrics` empty.\n- **Advanced** — non-empty `title` AND `style`; `vocal_gender` required when `instrumental=false`;\n  provide **exactly one** of `song_description` or `lyrics` (never both, never neither).","examples":[{"instrumental":false,"model":"V5_5","prompt":"Upbeat electronic dance track with bright synths"},{"instrumental":false,"lyrics":"","model":"V5_5","negative_tags":"","prompt":"","song_description":"A driving night highway vibe with restrained vocals","style":"Synthwave, 110 BPM, nostalgic neon pads","title":"Midnight Drive","vocal_gender":"f"}]},"MusicGenerationAccepted":{"properties":{"generation_id":{"type":"string","title":"Generation Id","description":"Poll this id with GET /v1/generate/music/{id}."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["pending"]},"estimated_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Time Seconds"},"billing_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Billing Mode"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"mode_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode Note"}},"type":"object","required":["generation_id"],"title":"MusicGenerationAccepted","description":"Async submit acknowledgement for `POST /v1/generate/music`."},"MusicGenerationStatus":{"properties":{"id":{"anyOf":[{"type":"integer"},{"type":"string"}],"title":"Id","description":"Internal row id (not the generation_id used to poll)."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["pending","processing","completed","failed"]},"progress":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Progress"},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt"},"style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Style"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"credits_deducted":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Credits Deducted"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"songs":{"items":{"$ref":"#/components/schemas/MusicSong"},"type":"array","title":"Songs"}},"type":"object","required":["id"],"title":"MusicGenerationStatus","description":"`GET /v1/generate/music/{id}` — job state and completed songs."},"MusicSong":{"properties":{"id":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"audio_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Url"},"stream_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stream Url"},"stream_audio_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stream Audio Url"},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"},"lyrics":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lyrics"},"style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Style"}},"type":"object","title":"MusicSong","description":"One completed song attached to a music generation."},"PremiumVideoAnswerAccepted":{"properties":{"question_id":{"type":"string","title":"Question Id","description":"Echoes the {question_id} path param."},"answer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Answer"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["answered"]},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","required":["question_id"],"title":"PremiumVideoAnswerAccepted","description":"`POST /v1/premium-video/questions/{question_id}/answer`."},"PremiumVideoOpenQuestion":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"question":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Question"},"options":{"items":{},"type":"array","title":"Options"},"window_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Window Expires At"}},"type":"object","title":"PremiumVideoOpenQuestion","description":"One open design-pass question a premium video order is waiting on."},"PremiumVideoOrderStatus":{"properties":{"order_id":{"type":"string","title":"Order Id","description":"Echoes the {order_id} path param."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"length_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Length Seconds"},"complexity_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Complexity Tier"},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format"},"sku_credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sku Credits"},"deposit_credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Deposit Credits"},"generation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generation Id"},"video_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Url"},"delivered_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delivered At"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"},"open_questions":{"items":{"$ref":"#/components/schemas/PremiumVideoOpenQuestion"},"type":"array","title":"Open Questions"},"action_required":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Required"}},"type":"object","required":["order_id"],"title":"PremiumVideoOrderStatus","description":"`GET /v1/premium-video/orders/{order_id}`."},"RemoveBackgroundRequest":{"properties":{"image_url":{"type":"string","title":"Image Url","description":"Public HTTPS URL of the image to cut out — from POST /v1/upload/media, or the output URL of an earlier generation. .jpg / .jpeg / .png / .webp only, max 5MB.","examples":["https://storage.kubeez.com/media-inputs/product.png"]}},"type":"object","required":["image_url"],"title":"RemoveBackgroundRequest","description":"Cut the subject out of an image (transparent PNG)."},"RenameAssetRequest":{"properties":{"name":{"type":"string","title":"Name","description":"New handle. Same rules as `POST /v1/assets`.","examples":["new-logo-name"]}},"type":"object","required":["name"],"title":"RenameAssetRequest","description":"Body for `PATCH /v1/assets/{id}`."},"SeparationAccepted":{"properties":{"separation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Separation Id","description":"Poll this id with GET /v1/generate/separation/{id}."},"task_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Id"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["pending"]},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","title":"SeparationAccepted","description":"Submit acknowledgement for `POST /v1/generate/separation`."},"SeparationRequest":{"properties":{"media_url":{"type":"string","title":"Media Url","description":"Audio file URL from POST /v1/upload/media.","examples":["https://storage.kubeez.com/media-inputs/song.mp3"]}},"type":"object","required":["media_url"],"title":"SeparationRequest","description":"Separate audio into vocals and instrumental tracks."},"SeparationStatus":{"properties":{"id":{"type":"string","title":"Id","description":"The separation_id used to look this job up."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["pending","completed","failed"]},"vocals_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vocals Url"},"instrumental_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instrumental Url"},"original_file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original File Url"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id"],"title":"SeparationStatus","description":"`GET /v1/generate/separation/{id}` — song_separations row."},"StoryGenerateRequest":{"properties":{"music":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Music"},"title":{"type":"string","title":"Title"},"aspect_ratio":{"type":"string","title":"Aspect Ratio","default":"9:16"},"language":{"type":"string","title":"Language","default":"en"},"style_bible":{"type":"string","title":"Style Bible"},"scenes":{"items":{"$ref":"#/components/schemas/StoryScene"},"type":"array","title":"Scenes"}},"type":"object","required":["title","style_bible","scenes"],"title":"StoryGenerateRequest"},"StoryJobAccepted":{"properties":{"story_job_id":{"type":"string","title":"Story Job Id","description":"Poll this id with GET /v1/story/{story_job_id}."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","examples":["queued"]},"scene_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Scene Count"},"total_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Seconds"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","required":["story_job_id"],"title":"StoryJobAccepted","description":"Submit acknowledgement for `POST /v1/story/generate`."},"StoryJobStatus":{"properties":{"id":{"type":"string","title":"Id","description":"The story_job_id used to look this job up."},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"final_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Final Url"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"scenes":{"items":{"$ref":"#/components/schemas/StorySceneStatus"},"type":"array","title":"Scenes"}},"type":"object","required":["id"],"title":"StoryJobStatus","description":"`GET /v1/story/{job_id}` — job row plus its scenes."},"StoryScene":{"properties":{"index":{"type":"integer","title":"Index"},"model":{"type":"string","title":"Model"},"prompt":{"type":"string","title":"Prompt"},"duration":{"type":"integer","title":"Duration"},"transition_in":{"type":"string","title":"Transition In","default":"cut"},"character_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Character Id"},"dialogue":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dialogue"},"keyframe_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Keyframe Url"}},"type":"object","required":["index","model","prompt","duration"],"title":"StoryScene"},"StorySceneStatus":{"properties":{"scene_index":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Scene Index"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"duration":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"StorySceneStatus","description":"One scene row within a story video job."},"UploadResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"urls":{"items":{"type":"string"},"type":"array","title":"Urls"},"bucket":{"type":"string","title":"Bucket"},"uploaded":{"type":"integer","title":"Uploaded"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"}},"type":"object","required":["success","urls","bucket","uploaded"],"title":"UploadResponse","description":"`POST /v1/upload/media`."},"VeoExtendRequest":{"properties":{"source_task_id":{"type":"string","minLength":8,"title":"Source Task Id","description":"Provider task id of the completed Veo 3.1 generation to extend. Retrieve it from `GET /v1/generate/media/{id}` → `source_task_id` (only populated for completed Veo 3.1 generations)."},"prompt":{"type":"string","maxLength":2000,"minLength":1,"title":"Prompt","description":"Text prompt for the extended clip."},"extend_model":{"type":"string","enum":["lite","fast","quality"],"title":"Extend Model","description":"Extend tier — flat price: lite=45, fast=75, quality=300 credits.","default":"fast"}},"type":"object","required":["source_task_id","prompt"],"title":"VeoExtendRequest","description":"Extend a completed Veo 3.1 video with a new clip (Veo 3.1 only).\n\nExtend is NOT a model you pick from `GET /v1/models` — it's an operation on a\ngeneration you already own. It will only accept Veo 3.1 sources (not other video\nmodels, not a clip that was itself an extend, and not 1080p outputs). Flat price\nper tier: `lite`=45, `fast`=75, `quality`=300 credits."},"ApiErrorBody":{"additionalProperties":true,"description":"Typical JSON error body from this API.","properties":{"error":{"description":"Machine-readable error code.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Optional hint for fixing the request.","title":"Hint"}},"required":["error"],"title":"ApiErrorBody","type":"object"},"RateLimitBody":{"additionalProperties":true,"properties":{"error":{"description":"Machine-readable error code.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Optional hint for fixing the request.","title":"Hint"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Max requests allowed in the window.","title":"Limit"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Remaining"},"reset_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"description":"Window size in seconds.","title":"Reset After"}},"required":["error"],"title":"RateLimitBody","type":"object"},"ValidationErrorItem":{"description":"One field-level failure inside `ValidationErrorBody.errors`.","properties":{"param":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Dotted path to the offending field.","title":"Param"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Why that field was rejected.","title":"Message"}},"title":"ValidationErrorItem","type":"object"},"ValidationErrorBody":{"additionalProperties":true,"description":"The body every SCHEMA failure returns: `400 invalid_request`.\n\nmain.py's RequestValidationError handler converts FastAPI's 422 into this\nenvelope, so 422 is unreachable in this service. custom_openapi() swaps the\nauto-injected `422 HTTPValidationError` for a 400 referencing this model —\npublishing a status code we never emit is exactly the untruthfulness the\ngenerated contract exists to remove.","properties":{"error":{"default":"invalid_request","description":"Always `invalid_request` here.","title":"Error","type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Human-readable detail.","title":"Message"},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Optional hint for fixing the request.","title":"Hint"},"param":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"The first offending field, echoed for convenience.","title":"Param"},"errors":{"anyOf":[{"items":{"$ref":"#/components/schemas/ValidationErrorItem"},"type":"array"},{"type":"null"}],"default":null,"description":"Every field-level failure in this request.","title":"Errors"}},"title":"ValidationErrorBody","type":"object"}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Kubeez API key (`sk_live_...`)."},"BearerApiKey":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"Raw key in Bearer form: `Authorization: Bearer sk_live_...`"}}},"tags":[{"name":"Models","description":"Discover enabled models, capabilities, and limits before calling generate endpoints."},{"name":"Media","description":"Start and monitor **image/video** jobs. **Required scope:** `generate:media`. After `POST /v1/generate/media`, poll `GET /v1/generate/media/{id}` until the job finishes."},{"name":"Music","description":"Start and monitor **music** jobs. **Required scope:** `generate:music`."},{"name":"Uploads","description":"Upload binary inputs; use returned URLs as `source_media_urls` on media generation. **Scope:** `generate:media`."},{"name":"Dialogue","description":"Multi-speaker TTS (not music). **Scopes:** `generate:speech` or `generate:music`. Track job via **Media** `GET /v1/generate/media/{id}`."},{"name":"Account","description":"Credit balance and generation history for the authenticated user."},{"name":"Assets","description":"Persistent per-user **Asset Library** — named, reusable images / videos / audio stored privately. Each entry returns a freshly signed CDN URL (1 h TTL) usable directly in `source_media_urls` on `POST /v1/generate/media`. **Scope:** `generate:media`."}],"servers":[{"url":"/","description":"This deployment (e.g. https://api.kubeez.com)"}],"security":[{"APIKeyHeader":[]},{"BearerApiKey":[]}]}