{"openapi":"3.1.0","info":{"title":"Ochra API","version":"v1","description":"The Ochra API: image, video and audio generation, your library, characters, assets and credits.\n\nAuthenticate every request with `Authorization: Bearer <key>`. Make keys in Settings → API keys; each acts as your account, spends its credits at the website's prices, and stops at its own 30-day credit limit. Call the API from your server: a request that carries an `Origin` header, as a web page's does, is refused.\n\nGeneration is asynchronous: a create call validates, prices and HOLDS the credits, then answers 202 with the task; poll GET /api/v1/tasks/{id} until the status is terminal. Create calls require an `Idempotency-Key` header.\n\nRate limits per key: 60 requests/minute and 2,000/day; 10 generation requests/minute; 30 imports/hour. Every key of one account together starts at most 300 generations a day. The whole table, with what each window counts, is `x-rate-limits`; a refused request is a 429 with `Retry-After`. Every response carries `X-Request-Id`; errors use one envelope (`ApiError`).\n\nThis document is served, always current, at GET /api/v1/openapi.json: without a key it describes what a key made in Settings can call; with your key, only what that key can call. `info.x-revision` (also the `ETag` header) changes whenever the API does; send it back as `If-None-Match` and an unchanged document answers 304. Models change more often than endpoints: the live catalogue — every model, its limits and its price — is GET /api/v1/models, and one tool's exact input schema is GET /api/v1/models/{tool}.","x-revision":"294eb22432492294"},"servers":[{"url":"https://www.ochra.io","description":"Ochra production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"account","description":"Acts as the key owner: their credits, tasks, outputs, characters and assets."}],"paths":{"/api/v1/me":{"get":{"operationId":"me","summary":"The key, its owner, balance and limits","description":"Who this key is and what it may spend: the key (never its secret) and what it may generate (`key.limits`: the kinds of media and the models it may use), the account it acts as, that account's credit balance, and the rate limits and in-flight allowance. It costs no credits, and it is the cheapest way to check a key works.","tags":["account"],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"},"example":{"api_version":"v1","channel":"api","key":{"id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","name":"Shop backend","prefix":"och_live_7hK2m","scopes":["read","generate"],"created_at":"2026-09-20T08:02:11.004512+00:00","last_used_at":"2026-09-23T10:14:03.482915+00:00","limits":{"kinds":null,"models":null,"blocked_models":[]},"cap_credits_30d":1000,"spent_credits_30d":240},"account":{"user_id":"2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204","email":"you@example.com","role":"user","nsfw_allowed":false,"trusted":false},"balance":{"balance":1240,"held":6,"allowance":0,"allowance_expires_at":null,"lifetime_added":5000,"lifetime_spent":3760},"limits":{"requests_per_minute":60,"requests_per_day":2000,"generations_per_minute":10,"max_inputs_per_task":4,"max_images_per_input":4,"max_in_flight_calls":8,"open_calls":1}}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error"],"x-endpoint-id":"me"}},"/api/v1/openapi.json":{"get":{"operationId":"openapi","summary":"This reference, for a machine","description":"The OpenAPI 3.1 document for this API, generated on the server from the same contract the endpoints enforce, so it is never behind them: a new endpoint, parameter or error appears here in the deploy that adds it. It describes only what THIS key can call — an endpoint outside the key's scopes, and the schemas only such endpoints use, are left out, and a key limited to some kinds of media is not shown POST /api/v1/images or /videos when it may not make images or video. `info.x-key-limits` repeats the key's limits (as GET /api/v1/me shows them). `info.x-revision` and the `ETag` header fingerprint the document; send the last one back as `If-None-Match` and an unchanged document answers 304 with no body, so checking for changes is cheap. Models change more often than endpoints and are not in here: the live catalogue — every model, its limits and its price — is GET /api/v1/models, and one tool's exact input schema is GET /api/v1/models/{tool}.","tags":["account"],"security":[{},{"bearerAuth":[]}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"openapi":{"type":"string","const":"3.1.0","description":"The OpenAPI version this document is written in."},"info":{"type":"object","properties":{"title":{"type":"string"},"version":{"type":"string","description":"The API version, `v1`."},"x-revision":{"type":"string","description":"A fingerprint of this exact document, the same value as the `ETag` header. It changes whenever an endpoint, a parameter or a description does."},"x-key-limits":{"$ref":"#/components/schemas/ApiKeyLimits"}},"required":["title","version","x-revision"],"additionalProperties":{},"description":"Title, API version and revision."},"servers":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"],"additionalProperties":{}},"description":"Where to send requests."},"paths":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Every endpoint this key may call, with its parameters, request body, responses and errors."},"components":{"type":"object","properties":{},"additionalProperties":{},"description":"Shared schemas, the security scheme and the reusable parameters."}},"required":["openapi","info","servers","paths","components"],"additionalProperties":{},"description":"An OpenAPI 3.1 document describing the endpoints the calling key may use."},"example":{"openapi":"3.1.0","info":{"title":"Brand API","version":"v1","x-revision":"7c1d0e5a9b2f4c68"},"servers":[{"url":"https://www.example.com","description":"Production"}],"security":[{"bearerAuth":[]}],"paths":{"/api/v1/me":{"get":{"operationId":"me","summary":"The key, its owner, balance and limits","tags":["account"],"responses":{"200":{"description":"OK"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key"}}}}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error"],"x-endpoint-id":"openapi"}},"/api/v1/models":{"get":{"operationId":"modelsList","summary":"Tools and models you can generate with","description":"Every tool this deployment sells, with its enabled models, each model's capability table (`caps`: ratios, resolutions, durations, reference limits, audio, and for text to speech `voice_count` — the voices themselves are GET /api/v1/models/{tool}/voices) and the default model. The catalogue fits one page, so `next_cursor` is always null. Tools the API refuses are not listed: those without a price (`run_any_llm`, `photoshoot_v2`) and any other tool this API does not run. Nor is anything outside this key's limits (GET /api/v1/me → `key.limits`): only the kinds and models it may use are listed, a tool it may use no model of is left out, and `default_model` is the first model it may use when the usual default is not one of them.\n\n`view=summary` answers the same catalogue in a few fields per model instead: every tool in `tools`, and in `data` one row per model (a model sold on several tools is one row naming them all), with what it makes, what one image or clip costs in credits (`credits.min`–`credits.max`; null where the price follows the length of the text or clip sent) and its key limits, paged with `cursor` and `limit`.","tags":["account"],"parameters":[{"name":"kind","in":"query","required":false,"description":"Only tools that produce this kind of media. Omit for all.","schema":{"type":"string","enum":["image","video","audio"]}},{"name":"view","in":"query","required":false,"description":"`full` (the default): every tool with its models' complete capability tables, on one page. `summary`: one short row per model — its tools, what it makes, its price range in credits and its key limits — paged with `cursor` and `limit`; GET /api/v1/models/{tool} has the rest.","schema":{"default":"full","type":"string","enum":["full","summary"]}},{"name":"cursor","in":"query","required":false,"description":"`view=summary` only: pass the previous page's `next_cursor` unchanged, with the same `kind`.","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"limit","in":"query","required":false,"description":"`view=summary` only: models per page, 1–200. Default 100, which holds the whole catalogue today.","schema":{"default":100,"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ModelTool"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},{"$ref":"#/components/schemas/ModelSummaryList"}]},"example":{"data":[{"tool_name":"by_prompt","kind":"image","display_name":"Image generator","description":"Text to image across the Seedream, Nano Banana, GPT Image, Grok and Qwen families.","default_model":"SEEDREAM_5_PRO","max_inputs":4,"models":[{"id":"SEEDREAM_5_PRO","display_name":"Seedream 5 Pro","group":"best","tags":["4K-ready","text rendering"],"is_default":true,"is_new":false,"trusted_only":false,"requires_character":false,"caps":{"durations":[],"duration_editable":true,"resolutions":["1K","1.5K","2K"],"default_resolution":"2K","ratios":["1:1","1:2","1:3","2:1","2:3","3:1","3:2","3:4","4:3","4:5","5:4","9:16","9:21","16:9","21:9"],"qualities":[],"modes":[],"max_reference_images":10,"max_reference_videos":0,"max_reference_video_seconds":0,"max_reference_audios":0,"max_reference_audio_seconds":0,"start_frame":false,"end_frame":false,"audio_input":false,"audio_generation":false,"audio_always_on":false,"audio_mutable":false,"negative_prompt":true,"seed":true,"camera_fixed":false,"prompt_extend":false,"multi_reference":true,"max_prompt_chars":null,"mutually_exclusive":[],"voice_count":0,"max_speech_chars":null}},{"id":"NANO_BANANA_PRO","display_name":"Nano Banana Pro","group":"best","tags":[],"is_default":false,"is_new":false,"trusted_only":false,"requires_character":false,"caps":{"durations":[],"duration_editable":true,"resolutions":["1K","2K","4K"],"default_resolution":"2K","ratios":["1:1","2:3","3:2","3:4","4:3","9:16","16:9","21:9"],"qualities":[],"modes":[],"max_reference_images":0,"max_reference_videos":0,"max_reference_video_seconds":0,"max_reference_audios":0,"max_reference_audio_seconds":0,"start_frame":false,"end_frame":false,"audio_input":false,"audio_generation":false,"audio_always_on":false,"audio_mutable":false,"negative_prompt":false,"seed":true,"camera_fixed":false,"prompt_extend":false,"multi_reference":true,"max_prompt_chars":null,"mutually_exclusive":[],"voice_count":0,"max_speech_chars":null}}]}],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"models.list"}},"/api/v1/models/{tool}":{"get":{"operationId":"modelsGet","summary":"One tool: models, caps and input schema","description":"Everything needed to build a raw call for POST /api/v1/generations: the enabled models and their caps, `input_schema` (the JSON Schema one element of `inputs[]` must satisfy) and `example_input`, a valid input for the default model. Per-model limits are not in the schema — they are in each model's `caps`, and a value outside them is a 422. An unknown, disabled or retired tool is a 404, and so is one this key may use no model of; the models listed are only those the key may use.","tags":["account"],"parameters":[{"name":"tool","in":"path","required":true,"description":"The tool name, e.g. `by_prompt`, `image_editor`, `videogen`, `text_to_video`.","schema":{"type":"string","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelToolDetail"},"example":{"tool_name":"text_to_video","kind":"video","display_name":"Text to video","description":"A clip from a prompt alone.","default_model":"seedance_2_5","max_inputs":1,"models":[{"id":"seedance_2_5","display_name":"Seedance 2.5","group":"best","tags":["audio"],"is_default":true,"is_new":true,"trusted_only":false,"requires_character":false,"caps":{"durations":[5,6,8,10,12,15,20,25,30],"duration_editable":true,"resolutions":["480p","720p","1080p"],"default_resolution":"720p","ratios":["adaptive","1:1","3:4","4:3","9:16","16:9","21:9"],"qualities":[],"modes":[],"max_reference_images":30,"max_reference_videos":10,"max_reference_video_seconds":30.2,"max_reference_audios":10,"max_reference_audio_seconds":0,"start_frame":false,"end_frame":true,"audio_input":false,"audio_generation":false,"audio_always_on":true,"audio_mutable":true,"negative_prompt":false,"seed":true,"camera_fixed":true,"prompt_extend":false,"multi_reference":true,"max_prompt_chars":null,"mutually_exclusive":[["ref_asset","ref_images"],["last_frame","ref_images"]],"voice_count":0,"max_speech_chars":null}}],"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompt":{"type":"string"},"model":{"type":"string","enum":["seedance_2_5","seedance_2_0"],"default":"seedance_2_0"},"ratio":{"type":"string","enum":["adaptive","1:1","16:9","9:16","4:3","3:4","21:9"],"default":"1:1"},"resolution":{"type":"string","enum":["480p","720p","1080p"],"default":"720p"},"duration":{"type":"number","default":5},"generate_audio":{"type":"boolean","default":false}},"required":["prompt"]},"example_input":{"prompt":"A red kite climbs over a windswept beach, handheld, golden hour","model":"seedance_2_5","ratio":"16:9","resolution":"720p","duration":5}}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"models.get"}},"/api/v1/models/{tool}/voices":{"get":{"operationId":"modelsVoices","summary":"The voices a text-to-speech model speaks with","description":"One model's voices, a page at a time, in the catalogue's order: each voice's `id` (the value to send as `voice` in a `text_to_speech` input), its name, its language and a one-line description of how it sounds. `language` keeps one language (case does not matter; `Chinese` matches `Chinese (Mandarin)`), and `languages` lists every language the model has voices in, with how many, whatever the filter. Send `next_cursor` back as `cursor` with the same `model` and `language` for the next page. GET /api/v1/models gives each model's `voice_count` instead of the voices, which keeps the catalogue small. A model with no voices to choose from answers an empty list. An unknown, disabled or retired tool or model is a 404, and so is one this key may not use.","tags":["account"],"parameters":[{"name":"tool","in":"path","required":true,"description":"The tool name, e.g. `by_prompt`, `image_editor`, `videogen`, `text_to_video`.","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"model","in":"query","required":true,"description":"The model whose voices to list: a model of the tool, as GET /api/v1/models/{tool} lists it.","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"language","in":"query","required":false,"description":"Only voices in this language, e.g. `English`, `Spanish`, `Chinese`. Case does not matter, and a language matches the longer names it starts (`Chinese` matches `Chinese (Mandarin)`). `languages` in the answer lists every one the model has.","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ModelVoice"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."},"tool_name":{"type":"string"},"model":{"type":"string","description":"The model these voices belong to."},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The `language` filter applied, or null for every language."},"total":{"type":"integer","minimum":0,"description":"Voices matching the filter, across every page."},"languages":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"The value to send as `language`."},"voices":{"type":"integer","minimum":1,"description":"How many of the model's voices speak it."}},"required":["language","voices"],"additionalProperties":false},"description":"Every language the model has voices in, whatever the filter, with how many: the values `language` takes."}},"required":["data","next_cursor","tool_name","model","language","total","languages"],"additionalProperties":false,"description":"A page of one model's voices, with every language it speaks."},"example":{"data":[{"id":"English_AssertiveQueen","name":"Assertive Queen","lang":"English","note":"An assertive yet guarded young adult queen with a general American accent, projecting authority while remaining cautious."},{"id":"English_Aussie_Bloke","name":"Aussie Bloke","lang":"English","note":"A friendly, bright adult male voice with a distinct Australian accent, conveying a cheerful and approachable tone."}],"next_cursor":"bzoy","tool_name":"text_to_speech","model":"minimax_speech_2_8_hd","language":"English","total":45,"languages":[{"language":"Arabic","voices":2},{"language":"Cantonese","voices":6},{"language":"Chinese (Mandarin)","voices":32},{"language":"English","voices":45},{"language":"French","voices":6},{"language":"German","voices":3},{"language":"Hindi","voices":3},{"language":"Japanese","voices":15},{"language":"Korean","voices":49},{"language":"Portuguese","voices":73},{"language":"Spanish","voices":47}]}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"models.voices"}},"/api/v1/price":{"post":{"operationId":"price","summary":"Price a request before you run it","description":"Validates and prices a raw tool call exactly as POST /api/v1/generations would — the same models, limits, prices and discounts as the website — and returns each input's price in credits, the total, your balance and whether you can afford it. Nothing is held, charged or created, and the price is not reserved: the generation prices the request again when it runs. To price a /images or /videos request, send that request itself with `?price_only=true`, or send the tool call it compiles to (a created task's `params` shows it). A request outside this key's limits is refused exactly as the generation would be — 403 `forbidden`, `meta.reason` `kind_not_allowed` or `model_not_allowed` — so your code learns it before it tries.","tags":["account"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tool_name":{"type":"string","minLength":1,"maxLength":64,"description":"The tool to run, e.g. `by_prompt` (text → image), `image_editor` (images + text → image), `by_ref`, `faceswap`, `text_to_video`, `videogen` (image/reference → video), `video_upscaler`, `music_generation`, `text_to_speech`. GET /api/v1/models lists exactly what this deployment sells. An unknown tool answers 404; a disabled or retired one is refused with 422 `validation_error`, and so are `run_any_llm` and `photoshoot_v2` (they have no price) and any other tool this API does not run."},"inputs":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"description":"One object per call. Each is validated against the tool's input schema (GET /api/v1/models/{tool} → `input_schema`); unknown keys are dropped. Every input is priced and held on its own and produces its own outputs. Image tools take 1–4 inputs; video and audio tools (`videogen`, `text_to_video`, `video_upscaler`, `music_generation`, `text_to_speech` — any tool whose `kind` is `video` or `audio`) take exactly 1; `max_inputs` in GET /api/v1/models says which. `batch_size` / `number_of_images` may not exceed 4, and on `by_prompt`, `image_editor` and `by_ref` must be 1: one input per image, as the website sends them (/images compiles `count` that way; `by_ref` defaults `batch_size` to 4, so send 1). Asset ports (`ref_asset`, `image_assets`, `ref_images`, `face_asset`, `last_frame`, …) take asset ids — your own (POST /api/v1/assets or /assets/import), a public asset, or an asset of a character shared with you — never URLs."}},"required":["tool_name","inputs"]},"example":{"tool_name":"by_prompt","inputs":[{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1},{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1}]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Price"},"example":{"tool_name":"by_prompt","items":[{"index":0,"model":"SEEDREAM_5_PRO","unit_price":3,"units":1,"total":3},{"index":1,"model":"SEEDREAM_5_PRO","unit_price":3,"units":1,"total":3}],"total":6,"balance":1240,"affordable":true,"shortfall":0}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `trusted_required`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","nsfw_disabled","trusted_required","not_implemented"],"x-endpoint-id":"price"}},"/api/v1/generations":{"post":{"operationId":"generationsCreate","summary":"Run any tool (raw tool call)","description":"The full-power endpoint: a `tool_name` and one input per call, shaped by GET /api/v1/models/{tool}. The request is validated and priced, the total is HELD from the key owner's balance, and the answer is 202 with the task while rendering continues in the background. Poll GET /api/v1/tasks/{id} every few seconds until `status` is terminal (`completed`, `partial`, `failed`, `cancelled`, `held_for_review`); images usually take 10–60 s, videos 1–6 minutes and occasionally longer. Each call is charged only for what it delivered and the rest is refunded automatically.\n\nRequires an `Idempotency-Key` header (a UUID you generate). Repeating a request with the same key returns the original task with 200, `Idempotency-Replayed: true` and `replayed: true` in the body, instead of charging twice. A reused key sent with a different request — another tool, prompt, option, asset or input count — is refused with 409 `conflict` (`meta.reason: \"idempotency_key_reused\"`) and charges nothing, so use a fresh key for every new request.\n\nWith `?price_only=true` nothing runs: the answer is 200 with what the request WOULD hold — the shape POST /api/v1/price answers, priced exactly as the request is — and nothing is held, no task is made, and the generation rate limits are not counted. The Idempotency-Key is still required and nothing is stored under it, so the real request may send the same one. `price_only` goes in the query string, once: sent twice, or in the JSON body, it is refused with 422 and nothing runs. Every refusal before the hold — validation (422), insufficient credits (402), a blocked account (403), the site's daily generation limit (503), too many unfinished calls on the account (429) — creates no task and charges nothing. So does a request outside the key's own limits (GET /api/v1/me → `key.limits`): a kind of media the key may not make is 403 `forbidden` with `meta.reason: \"kind_not_allowed\"`, and a model it may not use is 403 with `meta.reason: \"model_not_allowed\"` — the model named, under any of its names, or, for a request that names none, the default when the key may use no model of that tool (otherwise such a request runs on the key's own `default_model` from GET /api/v1/models/{tool}); `meta` also carries `kind`, `model` and `allowed_models`, the models of that tool the key may use (null when they could not be listed). Neither spends the Idempotency-Key: the same key can be retried once the request, or the key's limits, change.","tags":["account"],"parameters":[{"name":"price_only","in":"query","required":false,"description":"`true`: price this request instead of running it — 200 with the price (the shape POST /api/v1/price answers), nothing held, no task made. The Idempotency-Key is still required, and nothing is stored under it, so the real request may send the same one.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tool_name":{"type":"string","minLength":1,"maxLength":64,"description":"The tool to run, e.g. `by_prompt` (text → image), `image_editor` (images + text → image), `by_ref`, `faceswap`, `text_to_video`, `videogen` (image/reference → video), `video_upscaler`, `music_generation`, `text_to_speech`. GET /api/v1/models lists exactly what this deployment sells. An unknown tool answers 404; a disabled or retired one is refused with 422 `validation_error`, and so are `run_any_llm` and `photoshoot_v2` (they have no price) and any other tool this API does not run."},"inputs":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"description":"One object per call. Each is validated against the tool's input schema (GET /api/v1/models/{tool} → `input_schema`); unknown keys are dropped. Every input is priced and held on its own and produces its own outputs. Image tools take 1–4 inputs; video and audio tools (`videogen`, `text_to_video`, `video_upscaler`, `music_generation`, `text_to_speech` — any tool whose `kind` is `video` or `audio`) take exactly 1; `max_inputs` in GET /api/v1/models says which. `batch_size` / `number_of_images` may not exceed 4, and on `by_prompt`, `image_editor` and `by_ref` must be 1: one input per image, as the website sends them (/images compiles `count` that way; `by_ref` defaults `batch_size` to 4, so send 1). Asset ports (`ref_asset`, `image_assets`, `ref_images`, `face_asset`, `last_frame`, …) take asset ids — your own (POST /api/v1/assets or /assets/import), a public asset, or an asset of a character shared with you — never URLs."},"comment":{"description":"A note stored on the task (`comment`), up to 500 characters. Returned with the task and seen by our support team; not part of the prompt.","type":"string","maxLength":500},"project_id":{"type":"string","format":"uuid","description":"A project of yours, from GET /api/v1/projects: everything this makes is filed into it, as the website files work made inside a project (a generated file is in one project at a time). Another account's project, a deleted one, or one on a deployment without Projects is a 404 before anything is held. Omit to file nothing."},"template_id":{"type":"string","format":"uuid","description":"The template this request came from, for attribution only: it bumps the template's use count. It fills no inputs — send them in full."}},"required":["tool_name","inputs"]},"example":{"tool_name":"by_prompt","inputs":[{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1},{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1}]}}}},"responses":{"200":{"description":"A replay: this Idempotency-Key already created a task, which is returned as it is now (`replayed: true`). Nothing new was charged. Or, with `price_only=true`, what the request would hold — the shape POST /api/v1/price answers — with nothing held and no task made.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Task"},{"$ref":"#/components/schemas/Price"}]}}}},"202":{"description":"Accepted: the task exists, credits are held, rendering continues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"},"example":{"id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","unique_name":"by_prompt-3f9c1a7e","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","params":{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","negative_prompt":"","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1,"mode":"fast","quality":"medium"},"template_id":null,"comment":null,"progress":{"total":2,"done":0,"failed":0},"calls":[{"id":"c7e2b9d4-1f36-4a85-9c07-e4d8a2f6b531","index":0,"status":"processing","model":"SEEDREAM_5_PRO","error":null,"outputs":[]},{"id":"6e3c9a7f-2d58-4b14-8f96-b1a7e4c2d083","index":1,"status":"created","model":"SEEDREAM_5_PRO","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":6,"charged":0,"refunded":0,"held":6}}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Insufficient credits: `insufficient_credits`, `key_cap_reached`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `account_blocked`, `trusted_required`, `tool_restricted`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Unavailable: `maintenance`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer","apiCustomerGen","apiCustomerGenAccount","apiCustomerFleet"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","bad_request","validation_error","not_found","nsfw_disabled","insufficient_credits","key_cap_reached","account_blocked","trusted_required","tool_restricted","conflict","not_implemented","maintenance"],"x-endpoint-id":"generations.create"},"get":{"operationId":"generationsList","summary":"Your outputs (the library), newest first","description":"Every image, video and audio file the key owner has, with a signed URL, the prompt and the channel that made it. Hidden (deleted) outputs are not listed. Filter by `kind`, `tool` or `task_id`.","tags":["account"],"parameters":[{"name":"kind","in":"query","required":false,"description":"Only outputs of this media kind.","schema":{"type":"string","enum":["image","video","audio"]}},{"name":"tool","in":"query","required":false,"description":"Only this tool, e.g. `by_prompt`, `videogen`.","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"task_id","in":"query","required":false,"description":"Only the outputs of this task, in `position` order within each call.","schema":{"type":"string","format":"uuid"}},{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next (older) page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Generation"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},"example":{"data":[{"id":"e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63","task_id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","call_id":"c7e2b9d4-1f36-4a85-9c07-e4d8a2f6b531","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","position":0,"kind":"image","is_nsfw":false,"prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","channel":"api","created_at":"2026-09-23T10:14:31.905448+00:00","favourited_at":null,"media":{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"},"page_url":"https://www.example.com/my-generations"}],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"generations.list"}},"/api/v1/images":{"post":{"operationId":"imagesCreate","summary":"Generate images from a prompt, images or a character","description":"A convenience over POST /api/v1/generations. With a prompt alone it runs `by_prompt`; with `image_asset_ids` or a `character_id` it runs `image_editor`, the character's identity sheet first. `count` images are `count` calls of one image each, as the website sends them: each is priced and held on its own, and one that fails is refunded on its own. The request is validated and priced, the total is HELD from the key owner's balance, and the answer is 202 with the task while rendering continues in the background. Poll GET /api/v1/tasks/{id} every few seconds until `status` is terminal (`completed`, `partial`, `failed`, `cancelled`, `held_for_review`); images usually take 10–60 s, videos 1–6 minutes and occasionally longer. Each call is charged only for what it delivered and the rest is refunded automatically.\n\nRequires an `Idempotency-Key` header (a UUID you generate). Repeating a request with the same key returns the original task with 200, `Idempotency-Replayed: true` and `replayed: true` in the body, instead of charging twice. A reused key sent with a different request — another tool, prompt, option, asset or input count — is refused with 409 `conflict` (`meta.reason: \"idempotency_key_reused\"`) and charges nothing, so use a fresh key for every new request.\n\nWith `?price_only=true` nothing runs: the answer is 200 with what the request WOULD hold — the shape POST /api/v1/price answers, priced exactly as the request is — and nothing is held, no task is made, and the generation rate limits are not counted. The Idempotency-Key is still required and nothing is stored under it, so the real request may send the same one. `price_only` goes in the query string, once: sent twice, or in the JSON body, it is refused with 422 and nothing runs. Every refusal before the hold — validation (422), insufficient credits (402), a blocked account (403), the site's daily generation limit (503), too many unfinished calls on the account (429) — creates no task and charges nothing. So does a request outside the key's own limits (GET /api/v1/me → `key.limits`): a kind of media the key may not make is 403 `forbidden` with `meta.reason: \"kind_not_allowed\"`, and a model it may not use is 403 with `meta.reason: \"model_not_allowed\"` — the model named, under any of its names, or, for a request that names none, the default when the key may use no model of that tool (otherwise such a request runs on the key's own `default_model` from GET /api/v1/models/{tool}); `meta` also carries `kind`, `model` and `allowed_models`, the models of that tool the key may use (null when they could not be listed). Neither spends the Idempotency-Key: the same key can be retried once the request, or the key's limits, change.","tags":["account"],"parameters":[{"name":"price_only","in":"query","required":false,"description":"`true`: price this request instead of running it — 200 with the price (the shape POST /api/v1/price answers), nothing held, no task made. The Idempotency-Key is still required, and nothing is stored under it, so the real request may send the same one.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":5000,"description":"What to make, in plain language. Up to 5,000 characters; a model may accept fewer (`caps.max_prompt_chars`)."},"model":{"description":"A model id from GET /api/v1/models/{tool} for `by_prompt` (no images) or `image_editor` (with images or a character). Default `SEEDREAM_5_PRO` — or, for a key whose limits exclude it, that tool's `default_model` as GET /api/v1/models/{tool} shows it to the key. A model that is not enabled for the tool answers 422; its caps decide which ratios, resolutions and durations are valid.","type":"string","minLength":1,"maxLength":64},"ratio":{"description":"Aspect ratio. Without images the default is `1:1` (on a model whose `caps.ratios` lacks it, the first ratio it lists); with images, the first image's ratio. `1:2`, `2:1`, `1:3`, `3:1`, `4:5`, `5:4` and `9:21` only on models that list them in `caps.ratios`.","type":"string","enum":["1:1","16:9","9:16","4:3","3:4","3:2","2:3","21:9","9:21","4:5","5:4","2:1","1:2","3:1","1:3"]},"resolution":{"description":"Output size tier (the tools' `tier`). Must be one the model lists in `caps.resolutions`. Default `2K`, or on a model without 2K its `caps.default_resolution`, else the first tier it lists. Some models price by tier — price it first.","type":"string","enum":["1K","1.5K","2K","3K","4K"]},"count":{"default":1,"description":"Images to make, 1–4. Default 1. Each image is its own call, priced and held on its own, as on the website: the hold is the price of one image times the count, and an image that fails is refunded.","type":"integer","minimum":1,"maximum":4},"image_asset_ids":{"description":"Source or reference images, in order (up to 16; a model may take fewer — `caps.max_reference_images`). Switches the request to `image_editor`. Your own asset ids (upload or import first), public assets, or assets of characters shared with you.","minItems":1,"maxItems":16,"type":"array","items":{"type":"string","format":"uuid"}},"character_id":{"type":"string","format":"uuid","description":"A character from GET /api/v1/characters. Its identity sheet (`generation_ports.identity_asset_id`) is sent as the FIRST reference image, ahead of `image_asset_ids`, and the request runs on `image_editor`. Required by the `CHARACTER_ID` and `CHARACTER_VARIATION` models."},"negative_prompt":{"description":"What to keep out of the image. Text-to-image only (no `image_asset_ids`, no `character_id`), on models whose `caps.negative_prompt` is true.","type":"string","maxLength":2000},"comment":{"description":"A note stored on the task (`comment`), up to 500 characters. Returned with the task and seen by our support team; not part of the prompt.","type":"string","maxLength":500},"project_id":{"type":"string","format":"uuid","description":"A project of yours, from GET /api/v1/projects: everything this makes is filed into it, as the website files work made inside a project (a generated file is in one project at a time). Another account's project, a deleted one, or one on a deployment without Projects is a 404 before anything is held. Omit to file nothing."}},"required":["prompt"]},"example":{"prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","model":"SEEDREAM_5_PRO","ratio":"16:9","resolution":"2K","count":2}}}},"responses":{"200":{"description":"A replay: this Idempotency-Key already created a task, which is returned as it is now (`replayed: true`). Nothing new was charged. Or, with `price_only=true`, what the request would hold — the shape POST /api/v1/price answers — with nothing held and no task made.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Task"},{"$ref":"#/components/schemas/Price"}]}}}},"202":{"description":"Accepted: the task exists, credits are held, rendering continues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"},"example":{"id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","unique_name":"by_prompt-3f9c1a7e","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","params":{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","negative_prompt":"","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1,"mode":"fast","quality":"medium"},"template_id":null,"comment":null,"progress":{"total":2,"done":0,"failed":0},"calls":[{"id":"c7e2b9d4-1f36-4a85-9c07-e4d8a2f6b531","index":0,"status":"processing","model":"SEEDREAM_5_PRO","error":null,"outputs":[]},{"id":"6e3c9a7f-2d58-4b14-8f96-b1a7e4c2d083","index":1,"status":"created","model":"SEEDREAM_5_PRO","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":6,"charged":0,"refunded":0,"held":6}}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Insufficient credits: `insufficient_credits`, `key_cap_reached`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `account_blocked`, `trusted_required`, `tool_restricted`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Unavailable: `maintenance`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer","apiCustomerGen","apiCustomerGenAccount","apiCustomerFleet"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","bad_request","validation_error","not_found","nsfw_disabled","insufficient_credits","key_cap_reached","account_blocked","trusted_required","tool_restricted","conflict","not_implemented","maintenance"],"x-endpoint-id":"images.create"}},"/api/v1/videos":{"post":{"operationId":"videosCreate","summary":"Generate a video from a prompt, frames, references or a character","description":"A convenience over POST /api/v1/generations for one clip. With a prompt alone it runs `text_to_video`; with a first/last frame, reference images or a `character_id` it runs `videogen`. The request is validated and priced, the total is HELD from the key owner's balance, and the answer is 202 with the task while rendering continues in the background. Poll GET /api/v1/tasks/{id} every few seconds until `status` is terminal (`completed`, `partial`, `failed`, `cancelled`, `held_for_review`); images usually take 10–60 s, videos 1–6 minutes and occasionally longer. Each call is charged only for what it delivered and the rest is refunded automatically.\n\nRequires an `Idempotency-Key` header (a UUID you generate). Repeating a request with the same key returns the original task with 200, `Idempotency-Replayed: true` and `replayed: true` in the body, instead of charging twice. A reused key sent with a different request — another tool, prompt, option, asset or input count — is refused with 409 `conflict` (`meta.reason: \"idempotency_key_reused\"`) and charges nothing, so use a fresh key for every new request.\n\nWith `?price_only=true` nothing runs: the answer is 200 with what the request WOULD hold — the shape POST /api/v1/price answers, priced exactly as the request is — and nothing is held, no task is made, and the generation rate limits are not counted. The Idempotency-Key is still required and nothing is stored under it, so the real request may send the same one. `price_only` goes in the query string, once: sent twice, or in the JSON body, it is refused with 422 and nothing runs. Every refusal before the hold — validation (422), insufficient credits (402), a blocked account (403), the site's daily generation limit (503), too many unfinished calls on the account (429) — creates no task and charges nothing. So does a request outside the key's own limits (GET /api/v1/me → `key.limits`): a kind of media the key may not make is 403 `forbidden` with `meta.reason: \"kind_not_allowed\"`, and a model it may not use is 403 with `meta.reason: \"model_not_allowed\"` — the model named, under any of its names, or, for a request that names none, the default when the key may use no model of that tool (otherwise such a request runs on the key's own `default_model` from GET /api/v1/models/{tool}); `meta` also carries `kind`, `model` and `allowed_models`, the models of that tool the key may use (null when they could not be listed). Neither spends the Idempotency-Key: the same key can be retried once the request, or the key's limits, change.","tags":["account"],"parameters":[{"name":"price_only","in":"query","required":false,"description":"`true`: price this request instead of running it — 200 with the price (the shape POST /api/v1/price answers), nothing held, no task made. The Idempotency-Key is still required, and nothing is stored under it, so the real request may send the same one.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":5000,"description":"What to make, in plain language. Up to 5,000 characters; a model may accept fewer (`caps.max_prompt_chars`)."},"model":{"description":"A model id from GET /api/v1/models/{tool} for `text_to_video` (no assets) or `videogen` (with a frame, references or a character). Default `seedance_2_5` — or, for a key whose limits exclude it, that tool's `default_model` as GET /api/v1/models/{tool} shows it to the key. A model that is not enabled for the tool answers 422; its caps decide which ratios, resolutions and durations are valid.","type":"string","minLength":1,"maxLength":64},"duration":{"description":"Clip length in seconds. Must be one the model lists in `caps.durations`. Default 5, or the model's shortest length when it has no 5-second clip. Longer clips cost more — price it first.","type":"number","enum":[4,5,6,8,10,12,15,20,25,30]},"resolution":{"description":"Output resolution. Must be one the model lists in `caps.resolutions`. Default the model's `caps.default_resolution` (720p on Seedance).","type":"string","enum":["360p","480p","512p","720p","768p","1080p","2k","4k"]},"ratio":{"description":"Aspect ratio. `adaptive` follows the first frame. Default `adaptive` when a frame, reference image or character is sent, otherwise `16:9` — or, on a model whose `caps.ratios` lacks that value, the first ratio it lists.","type":"string","enum":["adaptive","16:9","9:16","1:1","4:3","3:4","21:9"]},"first_frame_asset_id":{"type":"string","format":"uuid","description":"An image asset the clip starts from (the tool's `ref_asset`). Switches the request to `videogen`."},"last_frame_asset_id":{"type":"string","format":"uuid","description":"An image asset the clip ends on (`last_frame`). Requires `first_frame_asset_id` and a model whose `caps.end_frame` is true."},"reference_image_asset_ids":{"description":"Multimodal reference images (`ref_images`), up to 30, for models with `caps.max_reference_images` > 0 (Seedance). Name them in the prompt as \"Reference Image 1\", \"Reference Image 2\", … in this order — with `character_id` the sheet is Reference Image 1 and counts toward the limit, so these start at 2. On Seedance they cannot be combined with first/last frames (`caps.mutually_exclusive`). Switches the request to `videogen`.","minItems":1,"maxItems":30,"type":"array","items":{"type":"string","format":"uuid"}},"character_id":{"type":"string","format":"uuid","description":"A character from GET /api/v1/characters. Its identity sheet is sent as the FIRST reference image (`ref_images`, before `reference_image_asset_ids`), so the model must take reference images (`caps.max_reference_images` > 0, e.g. Seedance). Switches the request to `videogen`."},"generate_audio":{"description":"Whether the clip has sound. When omitted the tool default applies: on for `videogen`, off for `text_to_video`. Models with `caps.audio_generation` synthesise a soundtrack on request, usually at a higher price — price it first; models with `caps.audio_always_on` (Seedance) include one at no extra cost and `false` mutes it where `caps.audio_mutable` allows.","type":"boolean"},"comment":{"description":"A note stored on the task (`comment`), up to 500 characters. Returned with the task and seen by our support team; not part of the prompt.","type":"string","maxLength":500},"project_id":{"type":"string","format":"uuid","description":"A project of yours, from GET /api/v1/projects: everything this makes is filed into it, as the website files work made inside a project (a generated file is in one project at a time). Another account's project, a deleted one, or one on a deployment without Projects is a 404 before anything is held. Omit to file nothing."}},"required":["prompt"]},"example":{"prompt":"The camera slowly pushes in as the woman in Reference Image 1 turns toward the window and smiles","model":"seedance_2_5","duration":5,"resolution":"720p","character_id":"7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91","comment":"Campaign 14, shot 3"}}}},"responses":{"200":{"description":"A replay: this Idempotency-Key already created a task, which is returned as it is now (`replayed: true`). Nothing new was charged. Or, with `price_only=true`, what the request would hold — the shape POST /api/v1/price answers — with nothing held and no task made.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Task"},{"$ref":"#/components/schemas/Price"}]}}}},"202":{"description":"Accepted: the task exists, credits are held, rendering continues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"},"example":{"id":"b8d1e5f3-4a27-4c96-8e3b-7f2a9d6c1b48","unique_name":"videogen-8b2e4d1c","tool_name":"videogen","model":"seedance_2_5","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"The camera slowly pushes in as the woman in Reference Image 1 turns toward the window and smiles","params":{"prompt":"The camera slowly pushes in as the woman in Reference Image 1 turns toward the window and smiles","model":"seedance_2_5","duration":5,"resolution":"720p","ratio":"adaptive","generate_audio":true,"ref_images":["4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264"],"omni_task":"reference"},"template_id":null,"comment":"Campaign 14, shot 3","progress":{"total":1,"done":0,"failed":0},"calls":[{"id":"d2f6a9c3-8e41-4b17-9d5a-3c7e1b8f4a62","index":0,"status":"processing","model":"seedance_2_5","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":29,"charged":0,"refunded":0,"held":29}}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Insufficient credits: `insufficient_credits`, `key_cap_reached`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `account_blocked`, `trusted_required`, `tool_restricted`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Unavailable: `maintenance`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer","apiCustomerGen","apiCustomerGenAccount","apiCustomerFleet"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","bad_request","validation_error","not_found","nsfw_disabled","insufficient_credits","key_cap_reached","account_blocked","trusted_required","tool_restricted","conflict","not_implemented","maintenance"],"x-endpoint-id":"videos.create"}},"/api/v1/tasks":{"get":{"operationId":"tasksList","summary":"Your tasks, newest first","description":"The key owner's tasks from every channel — the website included — unless `channel` narrows it. Summaries only, with no signed URLs, so listing is cheap; GET /api/v1/tasks/{id} has the outputs. Follow `next_cursor` for older pages.","tags":["account"],"parameters":[{"name":"status","in":"query","required":false,"description":"Only tasks in this status.","schema":{"type":"string","enum":["created","processing","partial","completed","failed","cancelled","held_for_review"]}},{"name":"tool","in":"query","required":false,"description":"Only this tool, e.g. `by_prompt`, `videogen`.","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"channel","in":"query","required":false,"description":"Only tasks created through this channel. Omit for every task of the account, website included.","schema":{"type":"string","enum":["web","api","mcp","system"]}},{"name":"since","in":"query","required":false,"description":"Only rows created at or after this point: a window counted back from now (`1h`, `24h`, `7d`, `30d`, `90d`) or an ISO-8601 timestamp with a timezone, e.g. `2026-09-01T00:00:00Z`. URL-encode a `+` offset as `%2B` (a bare `+` in a query string reads as a space and is refused), or use `Z`.","schema":{"anyOf":[{"type":"string","enum":["1h","24h","7d","30d","90d"]},{"type":"string","format":"date-time"}]}},{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next (older) page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TaskSummary"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},"example":{"data":[{"id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","unique_name":"by_prompt-3f9c1a7e","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","status":"completed","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":"2026-09-23T10:14:31.905448+00:00","prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","template_id":null,"comment":null,"progress":{"total":2,"done":2,"failed":0},"credits":{"quoted":6,"charged":6,"refunded":0,"held":0}}],"next_cursor":"MjAyNi0wOS0yM1QxMDoxNDowMy40ODI5MTUrMDA6MDB8YTNmNWM4ZTEtN2QyNC00Yjk2LWIwZTItNWM5ZDFmNmE4YjM3"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"tasks.list"}},"/api/v1/tasks/{id}":{"get":{"operationId":"tasksGet","summary":"A task: status, calls, outputs and cost","description":"Poll this after creating a generation. `outputs` lists every delivered file with a signed URL (lifetime `url_ttl`). Read results from `outputs`, not from `status`: a call that timed out can still deliver later, after its refund. `cost.credits` shows how the hold resolved (what was held at the start is `charged + refunded + held`). Only the key owner's tasks — any other id is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"},"example":{"id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","unique_name":"by_prompt-3f9c1a7e","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","status":"completed","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":"2026-09-23T10:14:31.905448+00:00","prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","params":{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","negative_prompt":"","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1,"mode":"fast","quality":"medium"},"template_id":null,"comment":null,"progress":{"total":2,"done":2,"failed":0},"calls":[{"id":"c7e2b9d4-1f36-4a85-9c07-e4d8a2f6b531","index":0,"status":"completed","model":"SEEDREAM_5_PRO","error":null,"outputs":[{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"}]},{"id":"6e3c9a7f-2d58-4b14-8f96-b1a7e4c2d083","index":1,"status":"completed","model":"SEEDREAM_5_PRO","error":null,"outputs":[{"asset_id":"9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70","kind":"image","mime":"image/png","bytes":3027118,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ.png","preview_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/preview.webp","thumb_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"}]}],"outputs":[{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","generation_id":"e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63"},{"asset_id":"9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70","kind":"image","mime":"image/png","bytes":3027118,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ.png","preview_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/preview.webp","thumb_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","generation_id":"f4a2c8e6-9b13-4d77-a260-8e5b1c3d9f24"}],"cost":{"credits":{"quoted":6,"charged":6,"refunded":0,"held":0}}}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"tasks.get"}},"/api/v1/tasks/batch":{"get":{"operationId":"tasksBatch","summary":"Several tasks by id, each in full","description":"Up to 12 of the key owner's tasks by id (`task_ids`, comma-separated), each exactly as GET /api/v1/tasks/{id} answers it, in the order asked — for polling a batch of generations with one request. `all_finished` is true once every one has a terminal status. An id that is not the key owner's, or is deleted, never fails the batch: it is listed in `not_found` and the rest come back.","tags":["account"],"parameters":[{"name":"task_ids","in":"query","required":true,"description":"1–12 task ids, comma-separated, each once.","schema":{"type":"string","minLength":1,"maxLength":480}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskBatch"},"example":{"data":[{"id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","unique_name":"by_prompt-3f9c1a7e","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","status":"completed","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":"2026-09-23T10:14:31.905448+00:00","prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","params":{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","negative_prompt":"","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1,"mode":"fast","quality":"medium"},"template_id":null,"comment":null,"progress":{"total":2,"done":2,"failed":0},"calls":[{"id":"c7e2b9d4-1f36-4a85-9c07-e4d8a2f6b531","index":0,"status":"completed","model":"SEEDREAM_5_PRO","error":null,"outputs":[{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"}]},{"id":"6e3c9a7f-2d58-4b14-8f96-b1a7e4c2d083","index":1,"status":"completed","model":"SEEDREAM_5_PRO","error":null,"outputs":[{"asset_id":"9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70","kind":"image","mime":"image/png","bytes":3027118,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ.png","preview_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/preview.webp","thumb_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"}]}],"outputs":[{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","generation_id":"e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63"},{"asset_id":"9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70","kind":"image","mime":"image/png","bytes":3027118,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ.png","preview_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/preview.webp","thumb_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","generation_id":"f4a2c8e6-9b13-4d77-a260-8e5b1c3d9f24"}],"cost":{"credits":{"quoted":6,"charged":6,"refunded":0,"held":0}}},{"id":"b8d1e5f3-4a27-4c96-8e3b-7f2a9d6c1b48","unique_name":"videogen-8b2e4d1c","tool_name":"videogen","model":"seedance_2_5","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"The camera slowly pushes in as the woman in Reference Image 1 turns toward the window and smiles","params":{"prompt":"The camera slowly pushes in as the woman in Reference Image 1 turns toward the window and smiles","model":"seedance_2_5","duration":5,"resolution":"720p","ratio":"adaptive","generate_audio":true,"ref_images":["4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264"],"omni_task":"reference"},"template_id":null,"comment":"Campaign 14, shot 3","progress":{"total":1,"done":0,"failed":0},"calls":[{"id":"d2f6a9c3-8e41-4b17-9d5a-3c7e1b8f4a62","index":0,"status":"processing","model":"seedance_2_5","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":29,"charged":0,"refunded":0,"held":29}}}],"not_found":[],"all_finished":false}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"tasks.batch"}},"/api/v1/generations/{id}":{"get":{"operationId":"generationsGet","summary":"One output with the prompt and parameters that made it","description":"The file with a fresh signed URL, plus the task behind it: the validated parameters that were sent, the model and the channel. Only the key owner's outputs.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationDetail"},"example":{"id":"e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63","task_id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","call_id":"c7e2b9d4-1f36-4a85-9c07-e4d8a2f6b531","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","position":0,"kind":"image","is_nsfw":false,"prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","channel":"api","created_at":"2026-09-23T10:14:31.905448+00:00","favourited_at":null,"media":{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"},"page_url":"https://www.example.com/my-generations","task":{"id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","unique_name":"by_prompt-3f9c1a7e","status":"completed","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","model":"SEEDREAM_5_PRO","params":{"positive_prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","negative_prompt":"","model":"SEEDREAM_5_PRO","ratio":"16:9","tier":"2K","batch_size":1,"mode":"fast","quality":"medium"},"comment":null,"template_id":null,"created_at":"2026-09-23T10:14:03.482915+00:00"}}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"generations.get"}},"/api/v1/generations/delete":{"post":{"operationId":"generationsDelete","summary":"Delete generated files from your library","description":"Deletes up to 50 of the key owner's generated files, by generation id, as Delete in the website's library does: each leaves the library, every picker and any project, and its short link stops opening (a URL already handed out lasts until its `expires_at`). It cannot be undone. Needs `confirm: true`. Every id must be the key owner's: one that is not, or does not exist, is a 404 naming it and nothing is deleted. A file a live character is made of — its identity sheet, cover or a reference photo — is a 409: it goes only with its character. Ids already deleted come back in `already_deleted`, so sending the same request again is safe. The task that made a file stays in GET /api/v1/tasks, because it backs the credit history. Spends no credits, but needs the `generate` scope.","tags":["account"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"generation_ids":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","format":"uuid"},"description":"Your generated files, by generation id: `id` from GET /api/v1/generations, or a task's `outputs[].generation_id`. Not an asset id. 1–50, each once."},"confirm":{"type":"boolean","const":true,"description":"Must be `true`: the files are removed from your library and you cannot bring them back. Send it only when the person has asked for exactly these files to be deleted."}},"required":["generation_ids","confirm"]},"example":{"generation_ids":["e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63","f4a2c8e6-9b13-4d77-a260-8e5b1c3d9f24"],"confirm":true}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationsDeleted"},"example":{"deleted":["e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63","f4a2c8e6-9b13-4d77-a260-8e5b1c3d9f24"],"already_deleted":[]}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","bad_request","conflict"],"x-endpoint-id":"generations.delete"}},"/api/v1/characters":{"get":{"operationId":"charactersList","summary":"Characters you own or that are shared with you","description":"The key owner's characters plus those other accounts shared with them (read-only). `generation_ports` holds the asset ids a generation should receive; `character_id` on /images and /videos wires them for you. A character without a sheet (`has_sheet: false`) cannot be used yet. No media URLs here — GET /api/v1/characters/{id} has them.","tags":["account"],"parameters":[{"name":"category","in":"query","required":false,"description":"Which kind of element: `character` (default), `location`, `prop`, or `all`. On a deployment without elements every row is a character.","schema":{"default":"character","type":"string","enum":["character","location","prop","all"]}},{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next (older) page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Character"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},"example":{"data":[{"id":"7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91","name":"Mira","category":"character","source":"upload","is_nsfw":false,"owner_user_id":"2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204","shared_with_me":false,"has_sheet":true,"generation_ports":{"identity_asset_id":"4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264","composition_asset_id":"6b9d3f1a-7e52-4c84-a3b6-d1f8e2c7a459"},"created_at":"2026-09-02T14:21:09.771203+00:00","updated_at":"2026-09-18T09:45:12.330917+00:00","page_url":"https://www.example.com/elements"}],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"characters.list"},"post":{"operationId":"charactersCreate","summary":"Create a character, as the Character Generator does","description":"Creates a character and starts the task that makes its identity sheet — the same three steps as the website's Character Generator, at the same credits. 1) This call makes the sheet on our character-sheet model (`GPT_IMAGE_2_5_SUNBURST`, `high`, 3 credits): `from: \"attributes\"` designs a new person (a `by_prompt` task) and `from: \"photos\"` builds it from 3–10 of your own photos of one person (an `image_editor` task); the answer is 202 with the character (`has_sheet: false` until the sheet lands) and the sheet task. 2) Poll GET /api/v1/tasks/{id} until the task is terminal; the character then has its sheet (`has_sheet: true`). A sheet the model's safety filter declines fails with `content_policy` and its credits are refunded. 3) POST /api/v1/characters/{id}/cover for its cover portrait (3 credits). A name already in use is a 409 (`meta.reason: \"name_taken\"`) before anything is held, and so is every other refusal below. The request is validated and priced, the total is HELD from the key owner's balance, and the answer is 202 with the task while rendering continues in the background. Poll GET /api/v1/tasks/{id} every few seconds until `status` is terminal (`completed`, `partial`, `failed`, `cancelled`, `held_for_review`); images usually take 10–60 s, videos 1–6 minutes and occasionally longer. Each call is charged only for what it delivered and the rest is refunded automatically.\n\nRequires an `Idempotency-Key` header (a UUID you generate). Repeating a request with the same key returns the original task with 200, `Idempotency-Replayed: true` and `replayed: true` in the body, instead of charging twice. A reused key sent with a different request — another tool, prompt, option, asset or input count — is refused with 409 `conflict` (`meta.reason: \"idempotency_key_reused\"`) and charges nothing, so use a fresh key for every new request.\n\nWith `?price_only=true` nothing runs: the answer is 200 with what the request WOULD hold — the shape POST /api/v1/price answers, priced exactly as the request is — and nothing is held, no task is made, and the generation rate limits are not counted. The Idempotency-Key is still required and nothing is stored under it, so the real request may send the same one. `price_only` goes in the query string, once: sent twice, or in the JSON body, it is refused with 422 and nothing runs. Every refusal before the hold — validation (422), insufficient credits (402), a blocked account (403), the site's daily generation limit (503), too many unfinished calls on the account (429) — creates no task and charges nothing. So does a request outside the key's own limits (GET /api/v1/me → `key.limits`): a kind of media the key may not make is 403 `forbidden` with `meta.reason: \"kind_not_allowed\"`, and a model it may not use is 403 with `meta.reason: \"model_not_allowed\"` — the model named, under any of its names, or, for a request that names none, the default when the key may use no model of that tool (otherwise such a request runs on the key's own `default_model` from GET /api/v1/models/{tool}); `meta` also carries `kind`, `model` and `allowed_models`, the models of that tool the key may use (null when they could not be listed). Neither spends the Idempotency-Key: the same key can be retried once the request, or the key's limits, change.","tags":["account"],"parameters":[{"name":"price_only","in":"query","required":false,"description":"`true`: price this request instead of running it — 200 with the price (the shape POST /api/v1/price answers), nothing held, no task made. The Idempotency-Key is still required, and nothing is stored under it, so the real request may send the same one.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","enum":["attributes","photos"],"description":"Both make the identity sheet on our character-sheet model (`GPT_IMAGE_2_5_SUNBURST`) at `high` quality, 16:9. `attributes`: design a new person from `attributes` (a `by_prompt` task). `photos`: a real person from 3–10 photos of them (`photo_asset_ids`, all of which go to the model; an `image_editor` task)."},"name":{"description":"The character's name, unique among your characters. Required with `photos`; with `attributes` one is picked when omitted. A name already in use is refused with 409 before anything is held.","type":"string","minLength":1,"maxLength":80},"attributes":{"description":"Required with `from: \"attributes\"`; not allowed with `photos`.","type":"object","properties":{"gender":{"type":"string","enum":["Female","Male"],"description":"`Female` or `Male`."},"age":{"type":"string","enum":["18-25","26-35","36-45","46-55","56-65","66-75","75-85","85+"],"description":"Apparent age band, e.g. `26-35`."},"origin":{"type":"string","enum":["Central American","Central Asian","East Asian","European","Middle Eastern","North African","North American","Oceanian / Pacific Islander","South American","South Asian","Southeast Asian"],"description":"Ethnic origin or look, e.g. `South Asian`."},"body_type":{"description":"Body type, e.g. `Fit`.","type":"string","enum":["Chubby","Curvy","Fit","Muscular","Normal","Slim"]},"eye_color":{"description":"Eye colour.","type":"string","enum":["Albino","Amber","Blue","Brown","Gray","Green","Hazel","Heterochromia","Red","Violet"]},"hair_color":{"description":"Hair colour.","type":"string","enum":["Black","Blonde","Blue","Brunette","Gray","Green","Honey","Ombre","Pink","Platinum","Red","Salt and Pepper","Violet"]},"hair_type":{"description":"Hair type, e.g. `Wavy`.","type":"string","enum":["Bald","Coily","Curly","Frizzy","Silky","Straight","Thick","Thin","Wavy"]},"hair_style":{"description":"Hair style, e.g. `Bob`.","type":"string","enum":["Afro","Bob","Bowl","Braids","Bun","Buzzcut","Caesar","Mohawk","Pigtails","Pixie","Ponytail","Shag","Undercut","Updo"]},"beard":{"description":"Beard style.","type":"string","enum":["Anchor Beard","Balbo Beard","Chevron Mustache","Chinstrap Beard","Circle Beard","Corporate Beard","Ducktail Beard","Friendly Mutton Chops","Full Beard","Garibaldi Beard","Goatee","Handlebar Mustache","Horseshoe Mustache","Imperial Mustache","Mutton Chops","Pencil Mustache","Soul Patch","Stubble Beard","Van Dyke Beard","Zappa Mustache"]},"beard_color":{"description":"Beard colour.","type":"string","enum":["Black","Blonde","Blue","Brunette","Gray","Green","Honey","Ombre","Pink","Platinum","Red","Salt and Pepper","Violet"]},"makeup":{"description":"Makeup style.","type":"string","enum":["Anime Makeup","Artistic Makeup","Avant-garde Makeup","Bohemian Makeup","Boho Makeup","Classic Makeup","Cut Crease Makeup","Dewy Makeup","Edgy Makeup","Festival Makeup","Glam Makeup","Glowy Makeup","Golden Makeup","Monochromatic Makeup","Natural Makeup","Party Makeup","Retro Makeup","Sunset Eye Makeup","Vintage Makeup","Watercolor Makeup"]}},"required":["gender","age","origin"]},"tier":{"default":"2K","description":"`attributes` only, and accepted for compatibility: a sheet has one size (1536x864) and one price, so it changes nothing.","type":"string","enum":["1K","2K"]},"photo_asset_ids":{"description":"Required with `from: \"photos\"`: 3–10 of your own image assets of ONE person (upload or import first), each once, as png, jpeg or webp (the sheet model reads only those). All of them go to the model, the portrait first.","minItems":3,"maxItems":10,"type":"array","items":{"type":"string","format":"uuid"}},"portrait_asset_id":{"type":"string","format":"uuid","description":"`photos` only: which photo is the portrait. One of `photo_asset_ids`; default the first."},"gender":{"description":"`photos` only: a hint for the sheet's wording. With `attributes`, the gender is `attributes.gender`.","type":"string","enum":["Female","Male"]},"project_id":{"type":"string","format":"uuid","description":"A project of yours, from GET /api/v1/projects: the new character is added to it, as a character made inside a project is on the website (its sheet is not filed). Another account's project, a deleted one, or one on a deployment without Projects is a 404 before anything is held."}},"required":["from"]},"example":{"from":"attributes","name":"Anaya","attributes":{"gender":"Female","age":"26-35","origin":"South Asian","hair_style":"Bob","eye_color":"Brown"}}}}},"responses":{"200":{"description":"A replay: this Idempotency-Key already created a task, which is returned as it is now (`replayed: true`). Nothing new was charged. Or, with `price_only=true`, what the request would hold — the shape POST /api/v1/price answers — with nothing held and no task made.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CharacterCreated"},{"$ref":"#/components/schemas/Price"}]}}}},"202":{"description":"Accepted: the task exists, credits are held, rendering continues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CharacterCreated"},"example":{"character":{"id":"d5a8e2c7-3f16-4b94-a7e0-1c9b6d4f8a32","name":"Anaya","category":"character","source":"options","is_nsfw":false,"owner_user_id":"2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204","shared_with_me":false,"has_sheet":false,"generation_ports":{"identity_asset_id":null,"composition_asset_id":null},"created_at":"2026-09-23T10:14:03.482915+00:00","updated_at":"2026-09-23T10:14:03.482915+00:00","page_url":"https://www.example.com/elements"},"task":{"id":"4b7e1d9a-6c38-4f25-8a91-e3d5c7b2f604","unique_name":"by_prompt-4b7e1d9a","tool_name":"by_prompt","model":"GPT_IMAGE_2_5_SUNBURST","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"A character reference sheet on a flat neutral grey seamless backdrop…","params":{"model":"GPT_IMAGE_2_5_SUNBURST","positive_prompt":"A character reference sheet on a flat neutral grey seamless backdrop…","ratio":"16:9","quality":"high","batch_size":1},"template_id":null,"comment":null,"progress":{"total":1,"done":0,"failed":0},"calls":[{"id":"a9c3f6e1-2d84-4b57-9e60-7f1b5d3a8c29","index":0,"status":"processing","model":"GPT_IMAGE_2_5_SUNBURST","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":3,"charged":0,"refunded":0,"held":3}}}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Insufficient credits: `insufficient_credits`, `key_cap_reached`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `account_blocked`, `trusted_required`, `tool_restricted`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Unavailable: `maintenance`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer","apiCustomerGen","apiCustomerGenAccount","apiCustomerFleet"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","bad_request","validation_error","not_found","nsfw_disabled","insufficient_credits","key_cap_reached","account_blocked","trusted_required","tool_restricted","conflict","not_implemented","maintenance"],"x-endpoint-id":"characters.create"}},"/api/v1/characters/{id}":{"get":{"operationId":"charactersGet","summary":"A character: sheet, cover and reference photos","description":"The character with signed URLs for its identity sheet, cover and uploaded reference photos. Owned or shared characters only; anything else is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CharacterDetail"},"example":{"id":"7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91","name":"Mira","category":"character","source":"upload","is_nsfw":false,"owner_user_id":"2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204","shared_with_me":false,"has_sheet":true,"generation_ports":{"identity_asset_id":"4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264","composition_asset_id":"6b9d3f1a-7e52-4c84-a3b6-d1f8e2c7a459"},"created_at":"2026-09-02T14:21:09.771203+00:00","updated_at":"2026-09-18T09:45:12.330917+00:00","page_url":"https://www.example.com/elements","description":"Late twenties, auburn bob, freckles; the campaign lead.","attributes":null,"sheet":{"asset_id":"4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264","kind":"image","mime":"image/png","bytes":1804211,"width":2048,"height":2048,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AU8rjW46kUxXnh23xaP44mRqvaKAWeHGap5vj0SCPrcFtWK6jw.png","preview_link":"https://www.example.com/m/AU8rjW46kUxXnh23xaP44mRqvaKAWeHGap5vj0SCPrcFtWK6jw/preview.webp","thumb_link":"https://www.example.com/m/AU8rjW46kUxXnh23xaP44mRqvaKAWeHGap5vj0SCPrcFtWK6jw/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","generation_id":"1c6e4a8f-9d23-4b75-8f0e-a2d7c5b9e316"},"cover":{"asset_id":"6b9d3f1a-7e52-4c84-a3b6-d1f8e2c7a459","kind":"image","mime":"image/png","bytes":1804211,"width":2048,"height":2048,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/6b9d3f1a-7e52-4c84-a3b6-d1f8e2c7a459/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/6b9d3f1a-7e52-4c84-a3b6-d1f8e2c7a459.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AWudPxp-UkyEo7bR-OLHpFlqvaKA4VV4BMlC5nPpaLjOqxl-wA.png","preview_link":"https://www.example.com/m/AWudPxp-UkyEo7bR-OLHpFlqvaKA4VV4BMlC5nPpaLjOqxl-wA/preview.webp","thumb_link":"https://www.example.com/m/AWudPxp-UkyEo7bR-OLHpFlqvaKA4VV4BMlC5nPpaLjOqxl-wA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","generation_id":"8e1a5c7d-2f94-4b36-9c8a-e5b3d7f1a620"},"references":[{"asset_id":"2d7f5b3e-8c16-4a49-b2d9-f4e6a1c8b573","kind":"image","mime":"image/jpeg","bytes":842119,"width":1536,"height":2048,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2d7f5b3e-8c16-4a49-b2d9-f4e6a1c8b573.jpg?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":null,"expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AS1_Wz6MFkpJstn05qHItXNqvaKAaLFPZEXr4-ipfRrrWtmLJw.jpg","preview_link":"https://www.example.com/m/AS1_Wz6MFkpJstn05qHItXNqvaKAaLFPZEXr4-ipfRrrWtmLJw/preview.webp","thumb_link":"https://www.example.com/m/AS1_Wz6MFkpJstn05qHItXNqvaKAaLFPZEXr4-ipfRrrWtmLJw/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","role":"face","position":0}]}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"characters.get"}},"/api/v1/characters/{id}/cover":{"post":{"operationId":"charactersCover","summary":"Make a character's cover portrait","description":"Step 3 of creating a character: edits its current identity sheet into a cover portrait (`image_editor` on `SEEDREAM_5_PRO`, 3 credits), exactly as the website does after a sheet lands, and makes it the cover when it arrives. Your own characters only (a shared one is a 404); a character with no sheet yet is a 422 — wait for its sheet task to finish. Also the way to remake a cover. The request is validated and priced, the total is HELD from the key owner's balance, and the answer is 202 with the task while rendering continues in the background. Poll GET /api/v1/tasks/{id} every few seconds until `status` is terminal (`completed`, `partial`, `failed`, `cancelled`, `held_for_review`); images usually take 10–60 s, videos 1–6 minutes and occasionally longer. Each call is charged only for what it delivered and the rest is refunded automatically.\n\nRequires an `Idempotency-Key` header (a UUID you generate). Repeating a request with the same key returns the original task with 200, `Idempotency-Replayed: true` and `replayed: true` in the body, instead of charging twice. A reused key sent with a different request — another tool, prompt, option, asset or input count — is refused with 409 `conflict` (`meta.reason: \"idempotency_key_reused\"`) and charges nothing, so use a fresh key for every new request.\n\nWith `?price_only=true` nothing runs: the answer is 200 with what the request WOULD hold — the shape POST /api/v1/price answers, priced exactly as the request is — and nothing is held, no task is made, and the generation rate limits are not counted. The Idempotency-Key is still required and nothing is stored under it, so the real request may send the same one. `price_only` goes in the query string, once: sent twice, or in the JSON body, it is refused with 422 and nothing runs. Every refusal before the hold — validation (422), insufficient credits (402), a blocked account (403), the site's daily generation limit (503), too many unfinished calls on the account (429) — creates no task and charges nothing. So does a request outside the key's own limits (GET /api/v1/me → `key.limits`): a kind of media the key may not make is 403 `forbidden` with `meta.reason: \"kind_not_allowed\"`, and a model it may not use is 403 with `meta.reason: \"model_not_allowed\"` — the model named, under any of its names, or, for a request that names none, the default when the key may use no model of that tool (otherwise such a request runs on the key's own `default_model` from GET /api/v1/models/{tool}); `meta` also carries `kind`, `model` and `allowed_models`, the models of that tool the key may use (null when they could not be listed). Neither spends the Idempotency-Key: the same key can be retried once the request, or the key's limits, change.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"price_only","in":"query","required":false,"description":"`true`: price this request instead of running it — 200 with the price (the shape POST /api/v1/price answers), nothing held, no task made. The Idempotency-Key is still required, and nothing is stored under it, so the real request may send the same one.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"A replay: this Idempotency-Key already created a task, which is returned as it is now (`replayed: true`). Nothing new was charged. Or, with `price_only=true`, what the request would hold — the shape POST /api/v1/price answers — with nothing held and no task made.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CharacterCreated"},{"$ref":"#/components/schemas/Price"}]}}}},"202":{"description":"Accepted: the task exists, credits are held, rendering continues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CharacterCreated"},"example":{"character":{"id":"7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91","name":"Mira","category":"character","source":"upload","is_nsfw":false,"owner_user_id":"2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204","shared_with_me":false,"has_sheet":true,"generation_ports":{"identity_asset_id":"4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264","composition_asset_id":"4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264"},"created_at":"2026-09-02T14:21:09.771203+00:00","updated_at":"2026-09-18T09:45:12.330917+00:00","page_url":"https://www.example.com/elements"},"task":{"id":"e6d2b8f4-1a73-4c59-b8e2-9d4f6a1c3e75","unique_name":"image_editor-e6d2b8f4","tool_name":"image_editor","model":"SEEDREAM_5_PRO","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"A portrait of the same woman shown in this sheet…","params":{"model":"SEEDREAM_5_PRO","image_assets":["4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264"],"ratio":"1:1","tier":"2K","number_of_images":1},"template_id":null,"comment":null,"progress":{"total":1,"done":0,"failed":0},"calls":[{"id":"2f8c5a1e-9b36-4d72-a4e8-c1f7d3b9e506","index":0,"status":"processing","model":"SEEDREAM_5_PRO","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":3,"charged":0,"refunded":0,"held":3}}}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Insufficient credits: `insufficient_credits`, `key_cap_reached`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `account_blocked`, `trusted_required`, `tool_restricted`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Unavailable: `maintenance`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer","apiCustomerGen","apiCustomerGenAccount","apiCustomerFleet"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","bad_request","validation_error","not_found","nsfw_disabled","insufficient_credits","key_cap_reached","account_blocked","trusted_required","tool_restricted","conflict","not_implemented","maintenance"],"x-endpoint-id":"characters.cover"}},"/api/v1/characters/{id}/sheet":{"post":{"operationId":"charactersSheet","summary":"Rebuild a character's sheet","description":"The website's \"Add photos & rebuild sheet\": optionally adds more of your own photos (`add_photo_asset_ids`, at most 10 photos in all), then rebuilds the identity sheet on our character-sheet model from the portrait and the most recently added photos, up to 10 (`image_editor`, 3 credits). A sheet the model's safety filter declines fails with `content_policy` and is refunded; different photos are the way forward. A designed character (`source: \"options\"`) keeps the sheet it was made with (422) — unless it has none because its sheet task failed: then this makes it again from its saved attributes, as the create did (no `add_photo_asset_ids`; a 409 while its sheet task is still running). Your own characters only. The request is validated and priced, the total is HELD from the key owner's balance, and the answer is 202 with the task while rendering continues in the background. Poll GET /api/v1/tasks/{id} every few seconds until `status` is terminal (`completed`, `partial`, `failed`, `cancelled`, `held_for_review`); images usually take 10–60 s, videos 1–6 minutes and occasionally longer. Each call is charged only for what it delivered and the rest is refunded automatically.\n\nRequires an `Idempotency-Key` header (a UUID you generate). Repeating a request with the same key returns the original task with 200, `Idempotency-Replayed: true` and `replayed: true` in the body, instead of charging twice. A reused key sent with a different request — another tool, prompt, option, asset or input count — is refused with 409 `conflict` (`meta.reason: \"idempotency_key_reused\"`) and charges nothing, so use a fresh key for every new request.\n\nWith `?price_only=true` nothing runs: the answer is 200 with what the request WOULD hold — the shape POST /api/v1/price answers, priced exactly as the request is — and nothing is held, no task is made, and the generation rate limits are not counted. The Idempotency-Key is still required and nothing is stored under it, so the real request may send the same one. `price_only` goes in the query string, once: sent twice, or in the JSON body, it is refused with 422 and nothing runs. Every refusal before the hold — validation (422), insufficient credits (402), a blocked account (403), the site's daily generation limit (503), too many unfinished calls on the account (429) — creates no task and charges nothing. So does a request outside the key's own limits (GET /api/v1/me → `key.limits`): a kind of media the key may not make is 403 `forbidden` with `meta.reason: \"kind_not_allowed\"`, and a model it may not use is 403 with `meta.reason: \"model_not_allowed\"` — the model named, under any of its names, or, for a request that names none, the default when the key may use no model of that tool (otherwise such a request runs on the key's own `default_model` from GET /api/v1/models/{tool}); `meta` also carries `kind`, `model` and `allowed_models`, the models of that tool the key may use (null when they could not be listed). Neither spends the Idempotency-Key: the same key can be retried once the request, or the key's limits, change.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"price_only","in":"query","required":false,"description":"`true`: price this request instead of running it — 200 with the price (the shape POST /api/v1/price answers), nothing held, no task made. The Idempotency-Key is still required, and nothing is stored under it, so the real request may send the same one.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"add_photo_asset_ids":{"description":"More of your own photos of the same person to add before rebuilding (a character holds at most 10). Added after the ones it has; the newest travel to the model with the portrait.","minItems":1,"maxItems":7,"type":"array","items":{"type":"string","format":"uuid"}},"model":{"description":"The model the sheet is rebuilt on: `GPT_IMAGE_2_5_SUNBURST`, the only one character sheets are made with (the default; there is nothing else to choose).","type":"string","enum":["GPT_IMAGE_2_5_SUNBURST"]},"gender":{"description":"A hint for the sheet's wording.","type":"string","enum":["Female","Male"]}}},"example":{"add_photo_asset_ids":["3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15"]}}}},"responses":{"200":{"description":"A replay: this Idempotency-Key already created a task, which is returned as it is now (`replayed: true`). Nothing new was charged. Or, with `price_only=true`, what the request would hold — the shape POST /api/v1/price answers — with nothing held and no task made.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CharacterCreated"},{"$ref":"#/components/schemas/Price"}]}}}},"202":{"description":"Accepted: the task exists, credits are held, rendering continues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CharacterCreated"},"example":{"character":{"id":"7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91","name":"Mira","category":"character","source":"upload","is_nsfw":false,"owner_user_id":"2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204","shared_with_me":false,"has_sheet":true,"generation_ports":{"identity_asset_id":"4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264","composition_asset_id":"6b9d3f1a-7e52-4c84-a3b6-d1f8e2c7a459"},"created_at":"2026-09-02T14:21:09.771203+00:00","updated_at":"2026-09-18T09:45:12.330917+00:00","page_url":"https://www.example.com/elements"},"task":{"id":"7d3f9b5c-8e21-4a64-9c17-b5e8a2d6f143","unique_name":"image_editor-7d3f9b5c","tool_name":"image_editor","model":"GPT_IMAGE_2_5_SUNBURST","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"A character sheet of the person in these photos…","params":{"model":"GPT_IMAGE_2_5_SUNBURST","image_assets":["2d7f5b3e-8c16-4a49-b2d9-f4e6a1c8b573","3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15"],"ratio":"16:9","quality":"high","number_of_images":1},"template_id":null,"comment":null,"progress":{"total":1,"done":0,"failed":0},"calls":[{"id":"c4e8a2f6-5b19-4d83-8f3a-6e2c9b7d1a58","index":0,"status":"processing","model":"GPT_IMAGE_2_5_SUNBURST","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":3,"charged":0,"refunded":0,"held":3}}}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Insufficient credits: `insufficient_credits`, `key_cap_reached`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `account_blocked`, `trusted_required`, `tool_restricted`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Unavailable: `maintenance`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer","apiCustomerGen","apiCustomerGenAccount","apiCustomerFleet"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","bad_request","validation_error","not_found","nsfw_disabled","insufficient_credits","key_cap_reached","account_blocked","trusted_required","tool_restricted","conflict","not_implemented","maintenance"],"x-endpoint-id":"characters.sheet"}},"/api/v1/photoshoots":{"post":{"operationId":"photoshootsCreate","summary":"A photo shoot of a person or a character","description":"The website's Photo Shoot: one subject — a photo (`image_asset_id`) or a character (`character_id`, its identity sheet) — and up to four pictures across the categories of GET /api/v1/photoshoot-categories, each a new pose in that category's scene, wardrobe and light. Composed exactly as the website composes it: a `photoshoot` task with one input per picture (3 credits each). The request is validated and priced, the total is HELD from the key owner's balance, and the answer is 202 with the task while rendering continues in the background. Poll GET /api/v1/tasks/{id} every few seconds until `status` is terminal (`completed`, `partial`, `failed`, `cancelled`, `held_for_review`); images usually take 10–60 s, videos 1–6 minutes and occasionally longer. Each call is charged only for what it delivered and the rest is refunded automatically.\n\nRequires an `Idempotency-Key` header (a UUID you generate). Repeating a request with the same key returns the original task with 200, `Idempotency-Replayed: true` and `replayed: true` in the body, instead of charging twice. A reused key sent with a different request — another tool, prompt, option, asset or input count — is refused with 409 `conflict` (`meta.reason: \"idempotency_key_reused\"`) and charges nothing, so use a fresh key for every new request.\n\nWith `?price_only=true` nothing runs: the answer is 200 with what the request WOULD hold — the shape POST /api/v1/price answers, priced exactly as the request is — and nothing is held, no task is made, and the generation rate limits are not counted. The Idempotency-Key is still required and nothing is stored under it, so the real request may send the same one. `price_only` goes in the query string, once: sent twice, or in the JSON body, it is refused with 422 and nothing runs. Every refusal before the hold — validation (422), insufficient credits (402), a blocked account (403), the site's daily generation limit (503), too many unfinished calls on the account (429) — creates no task and charges nothing. So does a request outside the key's own limits (GET /api/v1/me → `key.limits`): a kind of media the key may not make is 403 `forbidden` with `meta.reason: \"kind_not_allowed\"`, and a model it may not use is 403 with `meta.reason: \"model_not_allowed\"` — the model named, under any of its names, or, for a request that names none, the default when the key may use no model of that tool (otherwise such a request runs on the key's own `default_model` from GET /api/v1/models/{tool}); `meta` also carries `kind`, `model` and `allowed_models`, the models of that tool the key may use (null when they could not be listed). Neither spends the Idempotency-Key: the same key can be retried once the request, or the key's limits, change.","tags":["account"],"parameters":[{"name":"price_only","in":"query","required":false,"description":"`true`: price this request instead of running it — 200 with the price (the shape POST /api/v1/price answers), nothing held, no task made. The Idempotency-Key is still required, and nothing is stored under it, so the real request may send the same one.","schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"image_asset_id":{"type":"string","format":"uuid","description":"A photo of the person, your own asset (upload or import first). Send this or `character_id`."},"character_id":{"type":"string","format":"uuid","description":"A character (owned or shared with you) whose identity sheet is the subject. Send this or `image_asset_id`."},"shots":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"category_id":{"type":"string","format":"uuid","description":"A category from GET /api/v1/photoshoot-categories."},"count":{"type":"integer","minimum":1,"maximum":4,"description":"Pictures of this category, 1–4."}},"required":["category_id","count"],"description":"One category of the shoot and how many pictures of it."},"description":"The categories and counts, each category once. At most 4 pictures in all (one input each), each priced and held on its own."},"ratio":{"default":"1:1","description":"Aspect ratio of every picture. Default `1:1`, as on the website.","type":"string","enum":["16:9","1:1","1:2","1:3","21:9","2:1","2:3","3:1","3:2","3:4","4:3","4:5","5:4","9:16","9:21"]},"tier":{"default":"2K","description":"Size: `1K` or `2K` (default).","type":"string","enum":["1K","2K"]},"project_id":{"type":"string","format":"uuid","description":"A project of yours, from GET /api/v1/projects: everything this makes is filed into it, as the website files work made inside a project (a generated file is in one project at a time). Another account's project, a deleted one, or one on a deployment without Projects is a 404 before anything is held. Omit to file nothing."}},"required":["shots"]},"example":{"character_id":"7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91","shots":[{"category_id":"0badb198-e75a-4bd8-b928-aa400b0ff0f6","count":2}],"ratio":"4:5","tier":"2K"}}}},"responses":{"200":{"description":"A replay: this Idempotency-Key already created a task, which is returned as it is now (`replayed: true`). Nothing new was charged. Or, with `price_only=true`, what the request would hold — the shape POST /api/v1/price answers — with nothing held and no task made.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Task"},{"$ref":"#/components/schemas/Price"}]}}}},"202":{"description":"Accepted: the task exists, credits are held, rendering continues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"},"example":{"id":"1e9b6d3f-4c82-4a17-b5f9-8d2e7c4a6b03","unique_name":"photoshoot-1e9b6d3f","tool_name":"photoshoot","model":"SEEDREAM_5_PRO","status":"processing","channel":"api","api_key_id":"6f1c2a9e-3b7d-4e25-9a61-0c8f4d2b7e13","created_at":"2026-09-23T10:14:03.482915+00:00","started_at":"2026-09-23T10:14:04.117302+00:00","finished_at":null,"prompt":"A photograph of the SAME woman as in the reference image…","params":{"face_ref":"4f2b8d6e-3a91-4c57-9e1d-b7c5a3f8e264","category_id":"0badb198-e75a-4bd8-b928-aa400b0ff0f6","image_count":1,"ratio":"4:5","width":1856,"height":2304},"template_id":null,"comment":null,"progress":{"total":2,"done":0,"failed":0},"calls":[{"id":"9a5d2c8e-7f43-4b16-a0c9-3e8b1f6d4a27","index":0,"status":"processing","model":"SEEDREAM_5_PRO","error":null,"outputs":[]},{"id":"9a5d2c8e-7f43-4b16-a0c9-3e8b1f6d4a01","index":1,"status":"processing","model":"SEEDREAM_5_PRO","error":null,"outputs":[]}],"outputs":[],"cost":{"credits":{"quoted":6,"charged":0,"refunded":0,"held":6}}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`, `nsfw_disabled`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Insufficient credits: `insufficient_credits`, `key_cap_reached`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`, `account_blocked`, `trusted_required`, `tool_restricted`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"501":{"description":"Not implemented: `not_implemented`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Unavailable: `maintenance`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer","apiCustomerGen","apiCustomerGenAccount","apiCustomerFleet"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","bad_request","validation_error","not_found","nsfw_disabled","insufficient_credits","key_cap_reached","account_blocked","trusted_required","tool_restricted","conflict","not_implemented","maintenance"],"x-endpoint-id":"photoshoots.create"}},"/api/v1/photoshoot-categories":{"get":{"operationId":"photoshoot_categoriesList","summary":"Photo shoot categories","description":"Every category a photo shoot can use, with the id to send as `shots[].category_id`. Filter by `gender`. Adult categories are listed only to an account that holds the adult grant. One page: `next_cursor` is always null.","tags":["account"],"parameters":[{"name":"gender","in":"query","required":false,"description":"Only categories for this gender. Omit for both.","schema":{"type":"string","enum":["female","male"]}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PhotoshootCategory"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},"example":{"data":[{"id":"0badb198-e75a-4bd8-b928-aa400b0ff0f6","name":"Fashion","gender":"female","is_new":false},{"id":"96d85f72-42c1-4ea8-8f3d-0ab3e5b392c9","name":"Street Style","gender":"male","is_new":false}],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"photoshoot_categories.list"}},"/api/v1/projects":{"get":{"operationId":"projectsList","summary":"Your projects, newest first","description":"The key owner's projects — the website's folders of work — newest first, each with how many generated files, uploads and characters it holds and its page on the website. GET /api/v1/projects/{id} pages through one project's contents. Only the key owner's own projects: a project shared with them, another account's and a deleted one are a 404 — as on the website, where a project is shown to its owner alone. On a deployment without Projects every project endpoint is a 404.","tags":["account"],"parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next (older) page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Project"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},"example":{"data":[{"id":"5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","name":"Lighthouse campaign","created_at":"2026-09-21T08:02:44.517320+00:00","updated_at":"2026-09-21T08:02:44.517320+00:00","url":"https://www.example.com/projects/5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","counts":{"generations":2,"uploads":1,"characters":1}}],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"projects.list"},"post":{"operationId":"projectsCreate","summary":"Start a project","description":"Makes an empty project, exactly as \"New project\" on the website. A name already used by another of your projects (ignoring case) is a 409 with `meta.reason: \"name_taken\"`. The Idempotency-Key makes the project's id: retrying with the same key returns the project the first call made (200, `Idempotency-Replayed: true`) instead of making a second (deleted in between, it is made again under the same id). Spends no credits, but needs the `generate` scope. Fill it with POST /api/v1/projects/{id}/add, or pass its id as `project_id` to a generation.","tags":["account"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"The project's name, 1–80 characters (spaces at either end are trimmed). Unique among your projects, ignoring case: a name already in use is a 409 (`meta.reason: \"name_taken\"`)."}},"required":["name"]},"example":{"name":"Lighthouse campaign"}}}},"responses":{"200":{"description":"OK. Sent again with the same Idempotency-Key, the request is safe to repeat: it never makes a second project and changes nothing the first call already changed. A retried create answers with the project the first call made (`Idempotency-Replayed: true`); a retried add or remove counts only what it changed itself (0 once the first call landed); a retried delete is a 404, the project being gone.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"},"example":{"id":"5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","name":"Lighthouse campaign","created_at":"2026-09-21T08:02:44.517320+00:00","updated_at":"2026-09-21T08:02:44.517320+00:00","url":"https://www.example.com/projects/5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","counts":{"generations":0,"uploads":0,"characters":0}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","bad_request","not_found","conflict"],"x-endpoint-id":"projects.create"}},"/api/v1/projects/{id}":{"get":{"operationId":"projectsGet","summary":"A project and what it holds","description":"The project, with one page of one `section` of what it holds: `generations` (the generated files filed in it, as GET /api/v1/generations shows them, with download links), `uploads` (your own files added to it) or `characters` (the characters, locations and props added to it). Follow `next_cursor` with the same `section` for more. Only the key owner's own projects: a project shared with them, another account's and a deleted one are a 404 — as on the website, where a project is shown to its owner alone. On a deployment without Projects every project endpoint is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"section","in":"query","required":false,"description":"Which of the project's contents to page through: `generations` (default; the generated files filed in it), `uploads` (your own files added to it) or `characters` (the characters, locations and props added to it).","schema":{"default":"generations","type":"string","enum":["generations","uploads","characters"]}},{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next (older) page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDetail"},"example":{"project":{"id":"5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","name":"Lighthouse campaign","created_at":"2026-09-21T08:02:44.517320+00:00","updated_at":"2026-09-21T08:02:44.517320+00:00","url":"https://www.example.com/projects/5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","counts":{"generations":2,"uploads":1,"characters":1}},"section":"generations","generations":[{"id":"e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63","task_id":"a3f5c8e1-7d24-4b96-b0e2-5c9d1f6a8b37","call_id":"c7e2b9d4-1f36-4a85-9c07-e4d8a2f6b531","tool_name":"by_prompt","model":"SEEDREAM_5_PRO","position":0,"kind":"image","is_nsfw":false,"prompt":"A lighthouse on a basalt cliff at blue hour, long exposure, soft fog","channel":"api","created_at":"2026-09-23T10:14:31.905448+00:00","favourited_at":null,"media":{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"},"page_url":"https://www.example.com/projects/5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417"}],"uploads":[],"characters":[],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"projects.get"}},"/api/v1/projects/{id}/rename":{"post":{"operationId":"projectsRename","summary":"Rename a project","description":"Gives one of your projects a new name, as renaming it on the website does. A name another of your projects already has (ignoring case) is a 409 with `meta.reason: \"name_taken\"`; the project's own name, in any case, is fine. A project has a name and nothing else to edit. Spends no credits, but needs the `generate` scope. Requires an `Idempotency-Key` header (a UUID you generate). Sending the same request again with the same key is safe: it changes nothing the first call already changed — a retried add or remove counts only what it changes itself (0 once the first call landed), and a retried delete is a 404, the project being gone. Only the key owner's own projects: a project shared with them, another account's and a deleted one are a 404 — as on the website, where a project is shown to its owner alone. On a deployment without Projects every project endpoint is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"The project's name, 1–80 characters (spaces at either end are trimmed). Unique among your projects, ignoring case: a name already in use is a 409 (`meta.reason: \"name_taken\"`)."}},"required":["name"]},"example":{"name":"Lighthouse campaign — final"}}}},"responses":{"200":{"description":"OK. Sent again with the same Idempotency-Key, the request is safe to repeat: it never makes a second project and changes nothing the first call already changed. A retried create answers with the project the first call made (`Idempotency-Replayed: true`); a retried add or remove counts only what it changed itself (0 once the first call landed); a retried delete is a 404, the project being gone.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"},"example":{"id":"5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","name":"Lighthouse campaign — final","created_at":"2026-09-21T08:02:44.517320+00:00","updated_at":"2026-09-23T10:20:11.004512+00:00","url":"https://www.example.com/projects/5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","counts":{"generations":2,"uploads":1,"characters":1}}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","bad_request","conflict"],"x-endpoint-id":"projects.rename"}},"/api/v1/projects/{id}/add":{"post":{"operationId":"projectsAdd","summary":"Put generated files, uploads and characters into a project","description":"What the website's \"Add to project\" and the project page's pickers do: files your generated files (`generation_ids`; one project at a time, so this MOVES one that is in another project — `moved` counts them), your own uploads (`asset_ids`) and characters (`character_ids`, your own or shared with you) into the project. Every id must be one you can see: a single id that is not is a 404 and nothing is changed. Something already in the project is left as it is. Spends no credits, but needs the `generate` scope. Requires an `Idempotency-Key` header (a UUID you generate). Sending the same request again with the same key is safe: it changes nothing the first call already changed — a retried add or remove counts only what it changes itself (0 once the first call landed), and a retried delete is a 404, the project being gone. Only the key owner's own projects: a project shared with them, another account's and a deleted one are a 404 — as on the website, where a project is shown to its owner alone. On a deployment without Projects every project endpoint is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"generation_ids":{"description":"Generated files (generation ids, from GET /api/v1/generations or a task's `outputs[].generation_id`). Yours only. A generated file is in one project at a time, so adding one MOVES it out of any other project, as on the website. At most 100, each once.","minItems":1,"maxItems":100,"type":"array","items":{"type":"string","format":"uuid"}},"asset_ids":{"description":"Your own files (asset ids of uploads or imports — POST /api/v1/assets or /assets/import). A file can be in several projects at once. A generated output is added by its generation id, not its asset id. At most 100, each once.","minItems":1,"maxItems":100,"type":"array","items":{"type":"string","format":"uuid"}},"character_ids":{"description":"Characters, locations or props (ids from GET /api/v1/characters): your own, or ones shared with you. A character can be in several projects at once. At most 100, each once.","minItems":1,"maxItems":100,"type":"array","items":{"type":"string","format":"uuid"}}}},"example":{"generation_ids":["e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63","f4a2c8e6-9b13-4d77-a260-8e5b1c3d9f24"],"asset_ids":["3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15"],"character_ids":["7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91"]}}}},"responses":{"200":{"description":"OK. Sent again with the same Idempotency-Key, the request is safe to repeat: it never makes a second project and changes nothing the first call already changed. A retried create answers with the project the first call made (`Idempotency-Replayed: true`); a retried add or remove counts only what it changed itself (0 once the first call landed); a retried delete is a 404, the project being gone.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectItemsChanged"},"example":{"project":{"id":"5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","name":"Lighthouse campaign","created_at":"2026-09-21T08:02:44.517320+00:00","updated_at":"2026-09-21T08:02:44.517320+00:00","url":"https://www.example.com/projects/5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","counts":{"generations":2,"uploads":1,"characters":1}},"changed":{"generations":2,"uploads":1,"characters":1},"moved":0}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","bad_request"],"x-endpoint-id":"projects.add"}},"/api/v1/projects/{id}/remove":{"post":{"operationId":"projectsRemove","summary":"Take things out of a project","description":"Takes generated files, uploads and characters out of the project — the website's \"Remove from this project\". Only the membership goes: the files and characters themselves stay in your library. An id that is not in the project changes nothing and counts 0. Spends no credits, but needs the `generate` scope. Requires an `Idempotency-Key` header (a UUID you generate). Sending the same request again with the same key is safe: it changes nothing the first call already changed — a retried add or remove counts only what it changes itself (0 once the first call landed), and a retried delete is a 404, the project being gone. Only the key owner's own projects: a project shared with them, another account's and a deleted one are a 404 — as on the website, where a project is shown to its owner alone. On a deployment without Projects every project endpoint is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"generation_ids":{"description":"Generated files (generation ids, from GET /api/v1/generations or a task's `outputs[].generation_id`). Yours only. A generated file is in one project at a time, so adding one MOVES it out of any other project, as on the website. At most 100, each once.","minItems":1,"maxItems":100,"type":"array","items":{"type":"string","format":"uuid"}},"asset_ids":{"description":"Your own files (asset ids of uploads or imports — POST /api/v1/assets or /assets/import). A file can be in several projects at once. A generated output is added by its generation id, not its asset id. At most 100, each once.","minItems":1,"maxItems":100,"type":"array","items":{"type":"string","format":"uuid"}},"character_ids":{"description":"Characters, locations or props (ids from GET /api/v1/characters): your own, or ones shared with you. A character can be in several projects at once. At most 100, each once.","minItems":1,"maxItems":100,"type":"array","items":{"type":"string","format":"uuid"}}}},"example":{"generation_ids":["e1b7d3a9-6c42-4f08-b95e-2a7c4d1f8e63","f4a2c8e6-9b13-4d77-a260-8e5b1c3d9f24"],"asset_ids":["3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15"],"character_ids":["7a4c9e2b-1d85-4f36-b0a7-6e3d8c5f2b91"]}}}},"responses":{"200":{"description":"OK. Sent again with the same Idempotency-Key, the request is safe to repeat: it never makes a second project and changes nothing the first call already changed. A retried create answers with the project the first call made (`Idempotency-Replayed: true`); a retried add or remove counts only what it changed itself (0 once the first call landed); a retried delete is a 404, the project being gone.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectItemsChanged"},"example":{"project":{"id":"5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","name":"Lighthouse campaign","created_at":"2026-09-21T08:02:44.517320+00:00","updated_at":"2026-09-21T08:02:44.517320+00:00","url":"https://www.example.com/projects/5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","counts":{"generations":0,"uploads":0,"characters":0}},"changed":{"generations":2,"uploads":1,"characters":1},"moved":0}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","bad_request"],"x-endpoint-id":"projects.remove"}},"/api/v1/projects/{id}/delete":{"post":{"operationId":"projectsDelete","summary":"Delete a project (its contents are kept)","description":"Deletes the project for good, as the website's Delete does: only the folder goes. Its generated files, uploads and characters stay in your library, in no project. Needs `confirm: true` in the body. A project that is already gone is a 404. Spends no credits, but needs the `generate` scope and an `Idempotency-Key` header. Only the key owner's own projects: a project shared with them, another account's and a deleted one are a 404 — as on the website, where a project is shown to its owner alone. On a deployment without Projects every project endpoint is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"boolean","const":true,"description":"Must be `true`: the project is deleted for good. The generated files, uploads and characters in it are NOT deleted — they stay in your library, in no project — exactly as deleting a project on the website."}},"required":["confirm"]},"example":{"confirm":true}}}},"responses":{"200":{"description":"OK. Sent again with the same Idempotency-Key, the request is safe to repeat: it never makes a second project and changes nothing the first call already changed. A retried create answers with the project the first call made (`Idempotency-Replayed: true`); a retried add or remove counts only what it changed itself (0 once the first call landed); a retried delete is a 404, the project being gone.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotency-Replayed":{"$ref":"#/components/headers/IdempotencyReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDeleted"},"example":{"id":"5c1e8a3d-7b92-4f46-a0d3-e8b6c2f9a417","deleted":true,"generations_kept":2}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":true,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","bad_request"],"x-endpoint-id":"projects.delete"}},"/api/v1/assets":{"post":{"operationId":"assetsCreate","summary":"Reserve an upload and get a URL to PUT the file to","description":"Step 1 of 3 for your own reference media. Send the MIME type and size; get an `asset_id` and a signed upload URL. Step 2: PUT the raw bytes to `upload.url` with exactly `upload.headers` — no Authorization header, the URL is the credential. Step 3: POST /api/v1/assets/{id}/confirm. Files up to 50 MB (bytes never pass through this API, so the 4.5 MB request-body limit does not apply). The URL lasts 2 hours; an upload not confirmed by then is purged. Spends no credits, but needs the `generate` scope.","tags":["account"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mime":{"type":"string","minLength":3,"maxLength":100,"description":"The file's MIME type, one of `image/png`, `image/jpeg`, `image/webp`, `image/heic`, `image/heif`, `video/mp4`, `video/quicktime`, `video/webm`, `audio/mpeg`, `audio/wav`, `audio/mp4`. Send the same value as the `content-type` of the PUT. An unsupported type is a 400 `invalid_media_type`."},"bytes":{"type":"integer","minimum":1,"maximum":52428800,"description":"The file size in bytes, at most 52428800 (50 MB). Re-measured from the stored object on confirm."},"width":{"description":"Pixel width, if known. A claim: stored as sent, never measured.","type":"integer","minimum":1,"maximum":20000},"height":{"description":"Pixel height, if known. A claim: stored as sent, never measured.","type":"integer","minimum":1,"maximum":20000},"duration_ms":{"description":"Duration in milliseconds for video and audio, if known. A hint only: on confirm a video's length is read from the stored file, and tools that bill by clip length are priced from the file, never from this value.","type":"integer","minimum":0,"maximum":86400000}},"required":["mime","bytes"]},"example":{"mime":"image/jpeg","bytes":1048576,"width":1920,"height":1080}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetUpload"},"example":{"asset_id":"3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15","upload":{"url":"https://project-ref.supabase.co/storage/v1/object/upload/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15.jpg?token=eyJhbGciOiJIUzI1NiJ9.example","method":"PUT","headers":{"content-type":"image/jpeg","x-upsert":"false"},"expires_at":"2026-09-23T12:14:03.000Z"}}}}},"400":{"description":"Bad request: `invalid_media_type`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Payload too large: `payload_too_large`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","invalid_media_type","payload_too_large"],"x-endpoint-id":"assets.create"},"get":{"operationId":"assetsList","summary":"Your own files (uploads and imports), newest first","description":"Every file the key owner uploaded or imported and has not deleted, newest first, with a signed URL and the short links. Generated files are in GET /api/v1/generations. Uploads never confirmed are not listed. Filter by `kind`.","tags":["account"],"parameters":[{"name":"kind","in":"query","required":false,"description":"Only files of this media kind.","schema":{"type":"string","enum":["image","video","audio"]}},{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next (older) page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},"example":{"data":[{"asset_id":"7c2e9a5f-1b64-4d38-8e97-a5f3c1d6b820","kind":"image","mime":"image/jpeg","bytes":734002,"width":1920,"height":1080,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/7c2e9a5f-1b64-4d38-8e97-a5f3c1d6b820.jpg?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":null,"expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AXwuml8bZE04jpel88HWuCBqvaKA-lOqABl6i1iUuRpxVd49ZA.jpg","preview_link":"https://www.example.com/m/AXwuml8bZE04jpel88HWuCBqvaKA-lOqABl6i1iUuRpxVd49ZA/preview.webp","thumb_link":"https://www.example.com/m/AXwuml8bZE04jpel88HWuCBqvaKA-lOqABl6i1iUuRpxVd49ZA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","source":"import","origin_url":"https://cdn.example.com/moodboard/look-04.jpg","created_at":"2026-09-23T10:14:03.482915+00:00","confirmed_at":"2026-09-23T10:14:04.117302+00:00"},{"asset_id":"3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15","kind":"image","mime":"image/jpeg","bytes":1048576,"width":1920,"height":1080,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15.jpg?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":null,"expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q.jpg","preview_link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q/preview.webp","thumb_link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","source":"upload","origin_url":null,"created_at":"2026-09-23T10:14:03.482915+00:00","confirmed_at":"2026-09-23T10:14:04.117302+00:00"}],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"assets.list"}},"/api/v1/assets/{id}/confirm":{"post":{"operationId":"assetsConfirm","summary":"Confirm an upload so it can be used","description":"Reads the stored object back, re-checks its real type and size, reads a video's length from its header (a `duration_ms` sent at step 1 is only a hint), and makes the asset usable in any generation. A file whose real type or size breaks the rules is deleted and refused (400 `invalid_media_type` / 413 `payload_too_large`); an asset whose bytes never arrived is a 404. Confirming an already confirmed asset returns it unchanged.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"},"example":{"asset_id":"3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15","kind":"image","mime":"image/jpeg","bytes":1048576,"width":1920,"height":1080,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15.jpg?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":null,"expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q.jpg","preview_link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q/preview.webp","thumb_link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","source":"upload","origin_url":null,"created_at":"2026-09-23T10:14:03.482915+00:00","confirmed_at":"2026-09-23T10:14:04.117302+00:00"}}}},"400":{"description":"Bad request: `invalid_media_type`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Payload too large: `payload_too_large`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","invalid_media_type","payload_too_large"],"x-endpoint-id":"assets.confirm"}},"/api/v1/assets/import":{"post":{"operationId":"assetsImport","summary":"Import a file from an https URL","description":"The server downloads the URL once — at most 50 MB within 30 s — and stores it as your asset, ready to use: no upload steps. Only public https hosts; private, loopback and link-local addresses are refused (400), as are unsupported types (400) and oversized files (413). A Google Drive or Dropbox share link is fetched as its download link; a link that answers with a web page instead of the file (a folder, a private file, a sign-in) is a 400 `invalid_media_type` saying what to change, and one that answers 401, 403 or 404 is a 400 `bad_request` that does the same. A video's length is read from the downloaded file. A remote server that fails or times out is a 502.","tags":["account"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"format":"uri","description":"A public https URL the server fetches once: at most 52428800 bytes (50 MB), within 30 s, of a supported MIME type. Private, loopback and link-local addresses are refused. A Google Drive or Dropbox share link is fetched as its download link. The URL is kept on the asset as `origin_url`."}},"required":["url"]},"example":{"url":"https://cdn.example.com/moodboard/look-04.jpg"}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"},"example":{"asset_id":"7c2e9a5f-1b64-4d38-8e97-a5f3c1d6b820","kind":"image","mime":"image/jpeg","bytes":734002,"width":1920,"height":1080,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/7c2e9a5f-1b64-4d38-8e97-a5f3c1d6b820.jpg?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":null,"expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AXwuml8bZE04jpel88HWuCBqvaKA-lOqABl6i1iUuRpxVd49ZA.jpg","preview_link":"https://www.example.com/m/AXwuml8bZE04jpel88HWuCBqvaKA-lOqABl6i1iUuRpxVd49ZA/preview.webp","thumb_link":"https://www.example.com/m/AXwuml8bZE04jpel88HWuCBqvaKA-lOqABl6i1iUuRpxVd49ZA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","source":"import","origin_url":"https://cdn.example.com/moodboard/look-04.jpg","created_at":"2026-09-23T10:14:03.482915+00:00","confirmed_at":"2026-09-23T10:14:04.117302+00:00"}}}},"400":{"description":"Bad request: `bad_request`, `invalid_media_type`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Payload too large: `payload_too_large`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Bad gateway: `upstream_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer","apiCustomerImport"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","bad_request","invalid_media_type","payload_too_large","upstream_error"],"x-endpoint-id":"assets.import"}},"/api/v1/assets/{id}":{"get":{"operationId":"assetsGet","summary":"One of your assets with a fresh URL","description":"Any asset the key owner owns — uploads, imports and generated outputs — with a signed URL. Deleted assets are a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"},"example":{"asset_id":"3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15","kind":"image","mime":"image/jpeg","bytes":1048576,"width":1920,"height":1080,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15.jpg?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":null,"expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q.jpg","preview_link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q/preview.webp","thumb_link":"https://www.example.com/m/AT56HJ9bKE1jpOGfbCuNehVqvaKArOTOBupFFB8nE5wlRdwv3Q/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z","source":"upload","origin_url":null,"created_at":"2026-09-23T10:14:03.482915+00:00","confirmed_at":"2026-09-23T10:14:04.117302+00:00"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"assets.get"}},"/api/v1/assets/delete":{"post":{"operationId":"assetsDelete","summary":"Delete files you uploaded or imported","description":"Deletes up to 50 of the key owner's own files — uploads and imports — by asset id, as deleting an upload on the website does: each leaves GET /api/v1/assets, every picker and any project, and its short link stops opening (a URL already handed out lasts until its `expires_at`). It cannot be undone. Needs `confirm: true`. Every id must be one of the key owner's uploads or imports: another account's file, or one that does not exist, is a 404 naming it, and a generated file is a 422 (delete it by generation id at POST /api/v1/generations/delete) — either way nothing is deleted. Ids already deleted come back in `already_deleted`, so sending the same request again is safe. A photo a live character is made from — its face or a reference photo — is a 409 and nothing is deleted: it goes only with its character. Spends no credits, but needs the `generate` scope.","tags":["account"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"asset_ids":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","format":"uuid"},"description":"Your own files, by asset id: uploads and imports (GET /api/v1/assets). A generated file is deleted by its generation id at POST /api/v1/generations/delete instead. 1–50, each once."},"confirm":{"type":"boolean","const":true,"description":"Must be `true`: the files are removed from your library and you cannot bring them back. Send it only when the person has asked for exactly these files to be deleted."}},"required":["asset_ids","confirm"]},"example":{"asset_ids":["3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15","7c2e9a5f-1b64-4d38-8e97-a5f3c1d6b820"],"confirm":true}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetsDeleted"},"example":{"deleted":["3e7a1c9f-5b28-4d63-a4e1-9f6c2b8d7a15"],"already_deleted":["7c2e9a5f-1b64-4d38-8e97-a5f3c1d6b820"]}}}},"400":{"description":"Bad request: `bad_request`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Conflict: `conflict`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"generate","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found","bad_request","conflict"],"x-endpoint-id":"assets.delete"}},"/api/v1/media/{id}":{"get":{"operationId":"mediaGet","summary":"One file by asset id, with its short links","description":"Any file the key owner may see, by its asset id: a generated output, an upload or import, the sheet, cover or a reference photo of their own character or of one shared with them. `link` opens the original in a browser for 7–8 days; `preview_link` and `thumb_link` are stills of at most 1024 and 320 px. Anything else — another account's file, a deleted one — is a 404.","tags":["account"],"parameters":[{"name":"id","in":"path","required":true,"description":"The resource id (a UUID).","schema":{"type":"string","format":"uuid"}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Media"},"example":{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found: `not_found`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error","not_found"],"x-endpoint-id":"media.get"}},"/api/v1/media":{"get":{"operationId":"mediaList","summary":"Several files by asset id, as a gallery shows them","description":"Up to 24 files by asset id (`asset_ids`, comma-separated), in the order asked, each with its short links — what a gallery needs. One id that is not the key owner's, or is deleted, never fails the batch: it is listed in `not_found` and the rest come back.","tags":["account"],"parameters":[{"name":"asset_ids","in":"query","required":true,"description":"1–24 asset ids, comma-separated: outputs, your uploads, a character's sheet or cover — any `asset_id` another answer gave you.","schema":{"type":"string","minLength":1,"maxLength":960}},{"name":"url_ttl","in":"query","required":false,"description":"Lifetime of the signed media URLs in this response, in seconds: 60–28800, default 3600. Download once and keep the file, or reuse a URL until its `expires_at`: every freshly signed URL downloads the whole file again.","schema":{"default":3600,"type":"integer","minimum":60,"maximum":28800}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaBatch"},"example":{"data":[{"asset_id":"5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925","kind":"image","mime":"image/png","bytes":2914336,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/5d8e2a4c-7f19-4b63-8a0d-c6e1f3b7a925.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA.png","preview_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/preview.webp","thumb_link":"https://www.example.com/m/AV2OKkx_GUtjig3G4fO3qSVqvaKAJGDl5fduHom90OZ29nG4DA/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"},{"asset_id":"9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70","kind":"image","mime":"image/png","bytes":3027118,"width":2560,"height":1440,"duration_ms":null,"url":"https://project-ref.supabase.co/storage/v1/object/sign/assets-private/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/2026/09/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70/original.png?token=eyJhbGciOiJIUzI1NiJ9.example","preview_url":"https://project-ref.supabase.co/storage/v1/object/sign/previews/2b9e7c41-58d3-4a0f-8e6b-93c1d7a5f204/9b3f7e1d-2c58-4a94-b7e6-1d4a8c2f5e70.webp?token=eyJhbGciOiJIUzI1NiJ9.example","expires_at":"2026-09-23T11:14:40.000Z","link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ.png","preview_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/preview.webp","thumb_link":"https://www.example.com/m/AZs_fh0sWEqUt-YdSowvXnBqvaKA1l7IXh3VQ19hzb4g4WifXQ/thumb.webp","link_expires_at":"2026-10-01T00:00:00.000Z"}],"not_found":[]}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"media.list"}},"/api/v1/ledger":{"get":{"operationId":"ledgerList","summary":"Your credit ledger, newest first","description":"Every credit movement on the key owner's balance. A generation books a `spend` (the hold) when it is created and a `refund` for each failed call or undelivered image. Refund entries always read `web` or `system` in `channel`, whatever created the task — to attribute money to API use, go through the task (`cost` on GET /api/v1/tasks/{id}).","tags":["account"],"parameters":[{"name":"type","in":"query","required":false,"description":"Only this entry type: `topup`, `spend`, `refund`, `bonus`, `grant` or `expiry`.","schema":{"type":"string","enum":["topup","spend","refund","bonus","grant","expiry"]}},{"name":"task_id","in":"query","required":false,"description":"Only the entries of this task (its hold, and any refunds).","schema":{"type":"string","format":"uuid"}},{"name":"cursor","in":"query","required":false,"description":"Opaque. Pass the previous page's `next_cursor` unchanged to get the next (older) page; omit for the first page.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Rows per page, 1–100. Default 25. Out of range is a 422, never silently clamped.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LedgerEntry"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null on the last page."}},"required":["data","next_cursor"],"additionalProperties":false},"example":{"data":[{"id":"5f3b1d7e-9a42-4c68-b1e5-d8c2a6f4e937","type":"refund","source":"refund","kind":"refund","label":"Refund","amount":29,"resulting_amount":1240,"channel":"web","task_id":"b8d1e5f3-4a27-4c96-8e3b-7f2a9d6c1b48","call_id":"d2f6a9c3-8e41-4b17-9d5a-3c7e1b8f4a62","hold_id":"9e6c2a8d-4b15-4f73-a0d9-b2f8e5c1a746","reason":"call_failed","created_at":"2026-09-23T10:14:31.905448+00:00"},{"id":"0a5e8c2f-6d31-4b97-a8f4-c3b9e7d1f256","type":"spend","source":"task","kind":"spend","label":"Spend","amount":-29,"resulting_amount":1211,"channel":"api","task_id":"b8d1e5f3-4a27-4c96-8e3b-7f2a9d6c1b48","call_id":null,"hold_id":"9e6c2a8d-4b15-4f73-a0d9-b2f8e5c1a746","reason":null,"created_at":"2026-09-23T10:14:03.482915+00:00"},{"id":"c1d9f5a3-2e76-4b04-9f8c-6a3e1b7d5c28","type":"topup","source":"stripe","kind":"pack_purchase","label":"Credit pack","amount":1000,"resulting_amount":1240,"channel":"web","task_id":null,"call_id":null,"hold_id":null,"reason":null,"created_at":"2026-09-21T16:03:44.910023+00:00"}],"next_cursor":null}}}},"401":{"description":"Unauthenticated: `unauthenticated`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"$ref":"#/components/headers/WwwAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Forbidden: `forbidden`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation failed: `validation_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited: `rate_limited`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error: `internal_error`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"x-scope":"read","x-platform":false,"x-idempotent":false,"x-rate-limits":["apiCustomer"],"x-error-codes":["unauthenticated","forbidden","rate_limited","internal_error","validation_error"],"x-endpoint-id":"ledger.list"}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"An API key from Settings → API keys (`och_live_…`). Sent on every request, from your server; never in a URL or a web page."}},"schemas":{"ApiError":{"type":"object","properties":{"status_code":{"type":"integer","description":"The HTTP status, repeated."},"detail":{"type":"string","description":"Human-readable; the same text as `error.message`."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["bad_request","invalid_media_type","undecodable_image","unauthenticated","session_fingerprint_mismatch","nsfw_disabled","insufficient_credits","no_payment_method","key_cap_reached","forbidden","account_blocked","trusted_required","tool_restricted","device_limit_exceeded","not_eligible","not_found","conflict","version_locked","open_payments","already_active","payload_too_large","validation_error","rate_limited","internal_error","not_implemented","upstream_error","maintenance"],"description":"Machine code. Branch on this, never on the prose."},"message":{"type":"string","description":"Human-readable explanation, safe to show."},"details":{"description":"Per-field problems on a 422.","type":"array","items":{"$ref":"#/components/schemas/ApiErrorDetail"}},"meta":{"description":"Code-specific facts: `required`/`available` credits on 402, `retry_after_s` on 429, and on 403 a `reason` — `scope` (with the `required` scope), or `kind_not_allowed` / `model_not_allowed` when the key's limits (GET /api/v1/me → `key.limits`) exclude the request, with `kind`, `model`, `allowed_kinds` and `allowed_models` (the models of that tool this key may use, or null when they could not be listed).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["code","message"],"additionalProperties":false},"request_id":{"type":"string","description":"Matches the `X-Request-Id` header. Include it when you report a problem."}},"required":["status_code","detail","error","request_id"],"additionalProperties":false,"description":"The error envelope every failed request returns."},"ApiErrorDetail":{"type":"object","properties":{"field":{"description":"Dotted path of the offending field, e.g. `inputs.0.ratio`.","type":"string"},"message":{"type":"string","description":"What is wrong with it."},"node_id":{"description":"Legacy; unused by this API.","type":"string"}},"required":["message"],"additionalProperties":false},"Media":{"type":"object","properties":{"asset_id":{"type":"string","format":"uuid","description":"The asset id. Pass it to any asset port of a later generation."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"mime":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"MIME type of the original file, e.g. `image/png`, `video/mp4`."},"bytes":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Size of the original in bytes, when recorded."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel width, when known. Older uploads may have none."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel height, when known."},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Duration of video or audio in milliseconds, when known."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The original file. A signed URL valid until `expires_at`, or a permanent public URL. Null if the object could not be signed."},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A small preview (webp still, or a short mp4 for video) with the same lifetime as `url`, when one exists."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When `url` and `preview_url` stop working. Null for public URLs, which do not expire."},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short link to the original that opens in a browser until `link_expires_at` (7–8 days), however often it is clicked: each visit is sent on to a freshly signed copy. Give people this, not `url`. Null where this deployment has no links."},"preview_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A still of at most 1024 px (WebP): the picture itself, or a video's still. Null for audio, for a video with no still yet, and for formats that cannot be drawn (HEIC)."},"thumb_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The 320 px still, for grids. Null when `preview_link` is."},"link_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the three links stop working. The same all day: a link minted today is still this link later today."}},"required":["asset_id","kind","mime","bytes","width","height","duration_ms","url","preview_url","expires_at","link","preview_link","thumb_link","link_expires_at"],"additionalProperties":false,"description":"A media file with short-lived download URLs and short links that last a week."},"TaskOutput":{"type":"object","properties":{"asset_id":{"type":"string","format":"uuid","description":"The asset id. Pass it to any asset port of a later generation."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"mime":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"MIME type of the original file, e.g. `image/png`, `video/mp4`."},"bytes":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Size of the original in bytes, when recorded."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel width, when known. Older uploads may have none."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel height, when known."},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Duration of video or audio in milliseconds, when known."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The original file. A signed URL valid until `expires_at`, or a permanent public URL. Null if the object could not be signed."},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A small preview (webp still, or a short mp4 for video) with the same lifetime as `url`, when one exists."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When `url` and `preview_url` stop working. Null for public URLs, which do not expire."},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short link to the original that opens in a browser until `link_expires_at` (7–8 days), however often it is clicked: each visit is sent on to a freshly signed copy. Give people this, not `url`. Null where this deployment has no links."},"preview_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A still of at most 1024 px (WebP): the picture itself, or a video's still. Null for audio, for a video with no still yet, and for formats that cannot be drawn (HEIC)."},"thumb_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The 320 px still, for grids. Null when `preview_link` is."},"link_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the three links stop working. The same all day: a link minted today is still this link later today."},"generation_id":{"type":"string","format":"uuid","description":"The generation (library entry) this output is. GET /api/v1/generations/{id}."}},"required":["asset_id","kind","mime","bytes","width","height","duration_ms","url","preview_url","expires_at","link","preview_link","thumb_link","link_expires_at","generation_id"],"additionalProperties":false,"description":"An output of a task: a media file plus its generation id."},"TaskProgress":{"type":"object","properties":{"total":{"type":"integer","minimum":0,"description":"Calls in the task (one per input)."},"done":{"type":"integer","minimum":0,"description":"Calls that delivered."},"failed":{"type":"integer","minimum":0,"description":"Calls that failed and were refunded."}},"required":["total","done","failed"],"additionalProperties":false},"CallError":{"type":"object","properties":{"code":{"type":"string","description":"Why the call failed: `content_policy`, `validation`, `timeout`, `rate_limited`, `unavailable`, `storage_failed`, `internal`, `hold_expired` or `unknown`. `hold_expired` means the work took longer than its credits are held for; they were already returned to your balance."},"message":{"type":"string","description":"A customer-safe explanation. Never the provider's own wording."}},"required":["code","message"],"additionalProperties":false,"description":"Why a call failed."},"Call":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The call id."},"index":{"type":"integer","minimum":0,"description":"Position of the input this call ran, 0-based."},"status":{"type":"string","enum":["created","processing","completed","failed","cancelled","dead"],"description":"One call's state. `completed`, `failed`, `cancelled` and `dead` are terminal."},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The model this call ran on."},"error":{"anyOf":[{"$ref":"#/components/schemas/CallError"},{"type":"null"}],"description":"Null unless the call failed."},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"What this call delivered. A failed call can still carry an output recovered after its refund."}},"required":["id","index","status","model","error","outputs"],"additionalProperties":false,"description":"One call: one input of the task, priced, held and dispatched on its own."},"CreditsCost":{"type":"object","properties":{"quoted":{"type":"integer","minimum":0,"description":"The original hold: `charged + refunded + held`."},"charged":{"type":"integer","minimum":0,"description":"Credits actually taken, for what was delivered."},"refunded":{"type":"integer","minimum":0,"description":"Credits returned — failed calls and undelivered images."},"held":{"type":"integer","minimum":0,"description":"Still reserved for calls not yet finished. 0 once the task is terminal."}},"required":["quoted","charged","refunded","held"],"additionalProperties":false},"TaskCost":{"type":"object","properties":{"credits":{"$ref":"#/components/schemas/CreditsCost"}},"required":["credits"],"additionalProperties":false,"description":"What the task cost, in credits on your balance."},"TaskSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The task id. Poll GET /api/v1/tasks/{id}."},"unique_name":{"type":"string","description":"A short readable name, `<tool>-<8 hex>`."},"tool_name":{"type":"string","description":"The tool that ran."},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The model of the first input."},"status":{"type":"string","enum":["created","processing","partial","completed","failed","cancelled","held_for_review"],"description":"`created` (held, not started) → `processing` → one of the terminal states: `completed` (every call delivered), `partial` (some calls failed and were refunded), `failed` (nothing delivered, all refunded), `cancelled`, `held_for_review`. Stop polling at a terminal state."},"channel":{"type":"string","enum":["web","api","mcp","system"],"description":"Where the task was created: `web` (the website), `api` (REST), `mcp` (an app you connected, such as Claude), `system` (background jobs)."},"api_key_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The API key that created the task. Null for website tasks."},"created_at":{"type":"string","format":"date-time","description":"When the task was created and the credits were held."},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the first call was dispatched."},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the task reached a terminal status."},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The prompt, read from `positive_prompt`, else `prompt`, else `text`, else the comment."},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The template the task was attributed to."},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The comment sent with the request."},"progress":{"$ref":"#/components/schemas/TaskProgress"},"credits":{"$ref":"#/components/schemas/CreditsCost"}},"required":["id","unique_name","tool_name","model","status","channel","api_key_id","created_at","started_at","finished_at","prompt","template_id","comment","progress","credits"],"additionalProperties":false,"description":"A task in a list. GET /api/v1/tasks/{id} adds calls, outputs and the full cost."},"Task":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The task id. Poll GET /api/v1/tasks/{id}."},"unique_name":{"type":"string","description":"A short readable name, `<tool>-<8 hex>`."},"tool_name":{"type":"string","description":"The tool that ran."},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The model of the first input."},"status":{"type":"string","enum":["created","processing","partial","completed","failed","cancelled","held_for_review"],"description":"`created` (held, not started) → `processing` → one of the terminal states: `completed` (every call delivered), `partial` (some calls failed and were refunded), `failed` (nothing delivered, all refunded), `cancelled`, `held_for_review`. Stop polling at a terminal state."},"channel":{"type":"string","enum":["web","api","mcp","system"],"description":"Where the task was created: `web` (the website), `api` (REST), `mcp` (an app you connected, such as Claude), `system` (background jobs)."},"api_key_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The API key that created the task. Null for website tasks."},"created_at":{"type":"string","format":"date-time","description":"When the task was created and the credits were held."},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the first call was dispatched."},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the task reached a terminal status."},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The prompt, read from `positive_prompt`, else `prompt`, else `text`, else the comment."},"params":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The validated input of the first call, defaults applied — what was priced and sent."},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The template the task was attributed to."},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The comment sent with the request."},"progress":{"$ref":"#/components/schemas/TaskProgress"},"calls":{"type":"array","items":{"$ref":"#/components/schemas/Call"},"description":"One per input, in input order."},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/TaskOutput"},"description":"Everything the task delivered, call by call in output order. Read results from here, not from `status`."},"cost":{"$ref":"#/components/schemas/TaskCost"},"replayed":{"description":"Present, and true, only on the answer to a generation sent again with an Idempotency-Key that had already made this task: the original task, as it is now. Nothing new was made or charged.","type":"boolean","const":true}},"required":["id","unique_name","tool_name","model","status","channel","api_key_id","created_at","started_at","finished_at","prompt","params","template_id","comment","progress","calls","outputs","cost"],"additionalProperties":false,"description":"A generation task. Created by POST /generations, /images or /videos with 202; poll GET /api/v1/tasks/{id} until `status` is terminal."},"Generation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The generation id."},"task_id":{"type":"string","format":"uuid","description":"The task that produced it."},"call_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The call that produced it."},"tool_name":{"type":"string","description":"The tool that produced it."},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The model that produced it."},"position":{"type":"integer","minimum":0,"description":"Order within its call, 0-based."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"is_nsfw":{"type":"boolean","description":"Flagged as adult content."},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The task's prompt (same precedence as the task)."},"channel":{"type":"string","enum":["web","api","mcp","system"],"description":"Where the task was created: `web` (the website), `api` (REST), `mcp` (an app you connected, such as Claude), `system` (background jobs)."},"created_at":{"type":"string","format":"date-time","description":"When it was stored."},"favourited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When it was starred on the website."},"media":{"$ref":"#/components/schemas/Media"},"page_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where to see it on the website: its project's page when it is filed in one, otherwise your library."}},"required":["id","task_id","call_id","tool_name","model","position","kind","is_nsfw","prompt","channel","created_at","favourited_at","media","page_url"],"additionalProperties":false,"description":"One output in the library."},"GenerationTask":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The task id."},"unique_name":{"type":"string"},"status":{"type":"string","enum":["created","processing","partial","completed","failed","cancelled","held_for_review"],"description":"`created` (held, not started) → `processing` → one of the terminal states: `completed` (every call delivered), `partial` (some calls failed and were refunded), `failed` (nothing delivered, all refunded), `cancelled`, `held_for_review`. Stop polling at a terminal state."},"channel":{"type":"string","enum":["web","api","mcp","system"],"description":"Where the task was created: `web` (the website), `api` (REST), `mcp` (an app you connected, such as Claude), `system` (background jobs)."},"api_key_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The API key that created the task, when there was one."},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"params":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The validated input that was sent."},"comment":{"anyOf":[{"type":"string"},{"type":"null"}]},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The template the task was attributed to."},"created_at":{"type":"string","format":"date-time","description":"When the task was created."}},"required":["id","unique_name","status","channel","api_key_id","model","params","comment","template_id","created_at"],"additionalProperties":false,"description":"The task behind a generation."},"GenerationDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The generation id."},"task_id":{"type":"string","format":"uuid","description":"The task that produced it."},"call_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The call that produced it."},"tool_name":{"type":"string","description":"The tool that produced it."},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The model that produced it."},"position":{"type":"integer","minimum":0,"description":"Order within its call, 0-based."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"is_nsfw":{"type":"boolean","description":"Flagged as adult content."},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The task's prompt (same precedence as the task)."},"channel":{"type":"string","enum":["web","api","mcp","system"],"description":"Where the task was created: `web` (the website), `api` (REST), `mcp` (an app you connected, such as Claude), `system` (background jobs)."},"created_at":{"type":"string","format":"date-time","description":"When it was stored."},"favourited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When it was starred on the website."},"media":{"$ref":"#/components/schemas/Media"},"page_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where to see it on the website: its project's page when it is filed in one, otherwise your library."},"task":{"$ref":"#/components/schemas/GenerationTask"}},"required":["id","task_id","call_id","tool_name","model","position","kind","is_nsfw","prompt","channel","created_at","favourited_at","media","page_url","task"],"additionalProperties":false,"description":"One output with the prompt, parameters and channel of the task that made it."},"GenerationsDeleted":{"type":"object","properties":{"deleted":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The generated files this call deleted (generation ids, in the order sent): gone from your library, every picker and any project; their short links stop opening, and a URL already handed out lasts until its expires_at."},"already_deleted":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Generated files of yours that were already deleted before this call: nothing more to do."}},"required":["deleted","already_deleted"],"additionalProperties":false,"description":"What a delete removed. Sending the same request again is safe: everything comes back in `already_deleted`."},"CharacterPorts":{"type":"object","properties":{"identity_asset_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The identity sheet. Put it in an identity port (`face_asset`, `image_assets`, `ref_images`, `asset_image_1`) to keep the person's face; `character_id` on /images and /videos does this for you."},"composition_asset_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The cover image (falls back to the sheet), for composition ports such as `ref_asset`."}},"required":["identity_asset_id","composition_asset_id"],"additionalProperties":false,"description":"The asset ids a generation should receive for this character. Null until a sheet exists."},"Character":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The character id."},"name":{"type":"string"},"category":{"type":"string","enum":["character","location","prop"],"description":"`character`, `location` or `prop`."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"How it was made: `upload` (from photos) or `options` (designed from attributes)."},"is_nsfw":{"type":"boolean"},"owner_user_id":{"type":"string","format":"uuid","description":"The account that owns it."},"shared_with_me":{"type":"boolean","description":"True when another account shared it with the key owner (read-only)."},"has_sheet":{"type":"boolean","description":"Whether an identity sheet exists. Without one it cannot be used in a generation."},"generation_ports":{"$ref":"#/components/schemas/CharacterPorts"},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."},"page_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Your characters on the website, where this one is. Null for one shared with you, and where the website has no such page."}},"required":["id","name","category","source","is_nsfw","owner_user_id","shared_with_me","has_sheet","generation_ports","created_at","updated_at","page_url"],"additionalProperties":false,"description":"A character (persona) in a list."},"CharacterImage":{"type":"object","properties":{"asset_id":{"type":"string","format":"uuid","description":"The asset id. Pass it to any asset port of a later generation."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"mime":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"MIME type of the original file, e.g. `image/png`, `video/mp4`."},"bytes":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Size of the original in bytes, when recorded."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel width, when known. Older uploads may have none."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel height, when known."},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Duration of video or audio in milliseconds, when known."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The original file. A signed URL valid until `expires_at`, or a permanent public URL. Null if the object could not be signed."},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A small preview (webp still, or a short mp4 for video) with the same lifetime as `url`, when one exists."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When `url` and `preview_url` stop working. Null for public URLs, which do not expire."},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short link to the original that opens in a browser until `link_expires_at` (7–8 days), however often it is clicked: each visit is sent on to a freshly signed copy. Give people this, not `url`. Null where this deployment has no links."},"preview_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A still of at most 1024 px (WebP): the picture itself, or a video's still. Null for audio, for a video with no still yet, and for formats that cannot be drawn (HEIC)."},"thumb_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The 320 px still, for grids. Null when `preview_link` is."},"link_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the three links stop working. The same all day: a link minted today is still this link later today."},"generation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The generation this image is, when it was generated."}},"required":["asset_id","kind","mime","bytes","width","height","duration_ms","url","preview_url","expires_at","link","preview_link","thumb_link","link_expires_at","generation_id"],"additionalProperties":false,"description":"A character's sheet or cover image."},"CharacterReference":{"type":"object","properties":{"asset_id":{"type":"string","format":"uuid","description":"The asset id. Pass it to any asset port of a later generation."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"mime":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"MIME type of the original file, e.g. `image/png`, `video/mp4`."},"bytes":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Size of the original in bytes, when recorded."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel width, when known. Older uploads may have none."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel height, when known."},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Duration of video or audio in milliseconds, when known."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The original file. A signed URL valid until `expires_at`, or a permanent public URL. Null if the object could not be signed."},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A small preview (webp still, or a short mp4 for video) with the same lifetime as `url`, when one exists."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When `url` and `preview_url` stop working. Null for public URLs, which do not expire."},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short link to the original that opens in a browser until `link_expires_at` (7–8 days), however often it is clicked: each visit is sent on to a freshly signed copy. Give people this, not `url`. Null where this deployment has no links."},"preview_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A still of at most 1024 px (WebP): the picture itself, or a video's still. Null for audio, for a video with no still yet, and for formats that cannot be drawn (HEIC)."},"thumb_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The 320 px still, for grids. Null when `preview_link` is."},"link_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the three links stop working. The same all day: a link minted today is still this link later today."},"role":{"type":"string","enum":["reference","face"],"description":"`face` for the face crop, `reference` for the other photos."},"position":{"type":"integer","minimum":0,"description":"Order in the uploaded photo set."}},"required":["asset_id","kind","mime","bytes","width","height","duration_ms","url","preview_url","expires_at","link","preview_link","thumb_link","link_expires_at","role","position"],"additionalProperties":false,"description":"One uploaded reference photo."},"CharacterDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The character id."},"name":{"type":"string"},"category":{"type":"string","enum":["character","location","prop"],"description":"`character`, `location` or `prop`."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"How it was made: `upload` (from photos) or `options` (designed from attributes)."},"is_nsfw":{"type":"boolean"},"owner_user_id":{"type":"string","format":"uuid","description":"The account that owns it."},"shared_with_me":{"type":"boolean","description":"True when another account shared it with the key owner (read-only)."},"has_sheet":{"type":"boolean","description":"Whether an identity sheet exists. Without one it cannot be used in a generation."},"generation_ports":{"$ref":"#/components/schemas/CharacterPorts"},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."},"page_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Your characters on the website, where this one is. Null for one shared with you, and where the website has no such page."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Its description, as written, where there is one."},"attributes":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The designed attributes (`source` = `options`), e.g. hair, age, style."},"sheet":{"anyOf":[{"$ref":"#/components/schemas/CharacterImage"},{"type":"null"}],"description":"The current identity sheet."},"cover":{"anyOf":[{"$ref":"#/components/schemas/CharacterImage"},{"type":"null"}],"description":"The cover image."},"references":{"type":"array","items":{"$ref":"#/components/schemas/CharacterReference"},"description":"The uploaded photos, in order."}},"required":["id","name","category","source","is_nsfw","owner_user_id","shared_with_me","has_sheet","generation_ports","created_at","updated_at","page_url","description","attributes","sheet","cover","references"],"additionalProperties":false,"description":"One character with its sheet, cover and reference photos."},"CharacterCreated":{"type":"object","properties":{"character":{"$ref":"#/components/schemas/Character"},"task":{"$ref":"#/components/schemas/Task","description":"The task making the sheet (or the cover). Poll GET /api/v1/tasks/{id} until it is terminal."},"replayed":{"description":"Present, and true, only when an Idempotency-Key that had already made this task was sent again: nothing new was made or charged.","type":"boolean","const":true}},"required":["character","task"],"additionalProperties":false,"description":"A character and the task working on it: its identity sheet, its cover, or a rebuilt sheet."},"PhotoshootCategory":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The value to send as `shots[].category_id`."},"name":{"type":"string","description":"What the shoot looks like, e.g. \"Beach day\"."},"gender":{"type":"string","enum":["female","male"],"description":"Who the category is written for."},"is_new":{"type":"boolean"}},"required":["id","name","gender","is_new"],"additionalProperties":false,"description":"One photo shoot category."},"ProjectCounts":{"type":"object","properties":{"generations":{"type":"integer","minimum":0,"description":"Generated files filed in the project (deleted ones are not counted)."},"uploads":{"type":"integer","minimum":0,"description":"Your own files added to the project (deleted ones are not counted)."},"characters":{"type":"integer","minimum":0,"description":"Characters, locations and props added to the project that you can still see (deleted or no-longer-shared ones are not counted)."}},"required":["generations","uploads","characters"],"additionalProperties":false,"description":"How much the project holds."},"Project":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The project id. Pass it as `project_id` to a generation to file what it makes here."},"name":{"type":"string","description":"The project's name, as its owner typed it."},"created_at":{"type":"string","format":"date-time","description":"When the project was made."},"updated_at":{"type":"string","format":"date-time","description":"When it was made or last renamed."},"url":{"type":"string","description":"The project's page on the website."},"counts":{"$ref":"#/components/schemas/ProjectCounts"}},"required":["id","name","created_at","updated_at","url","counts"],"additionalProperties":false,"description":"A project: a folder of your work on the website — generated files, your own uploads and characters."},"ProjectDetail":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"section":{"type":"string","enum":["generations","uploads","characters"],"description":"Which list this page fills: `generations`, `uploads` or `characters`."},"generations":{"type":"array","items":{"$ref":"#/components/schemas/Generation"},"description":"`section: generations` — the generated files filed in the project, newest first, with download links. Empty for the other sections."},"uploads":{"type":"array","items":{"$ref":"#/components/schemas/Asset"},"description":"`section: uploads` — your own files added to the project, newest added first, with download links. Empty for the other sections."},"characters":{"type":"array","items":{"$ref":"#/components/schemas/Character"},"description":"`section: characters` — the characters, locations and props added to the project, newest added first. Empty for the other sections."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` (with the same `section`) for the next page. Null on the last page."}},"required":["project","section","generations","uploads","characters","next_cursor"],"additionalProperties":false,"description":"A project and one page of what it holds: its generated files, its uploads or its characters (`section`)."},"ProjectItemsChanged":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"changed":{"type":"object","properties":{"generations":{"type":"integer","minimum":0,"description":"Generated files put into (or taken out of) the project by this call."},"uploads":{"type":"integer","minimum":0,"description":"Your files put into (or taken out of) the project by this call."},"characters":{"type":"integer","minimum":0,"description":"Characters put into (or taken out of) the project by this call."}},"required":["generations","uploads","characters"],"additionalProperties":false,"description":"What this call changed. Ids already in the project (on an add) or not in it (on a remove) count 0: nothing to do."},"moved":{"type":"integer","minimum":0,"description":"An add only: how many of those generated files were in ANOTHER project and are now in this one instead. 0 on a remove."}},"required":["project","changed","moved"],"additionalProperties":false,"description":"What an add or a remove changed, and the project as it is now."},"ProjectDeleted":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The project that was deleted."},"deleted":{"type":"boolean","const":true},"generations_kept":{"type":"integer","minimum":0,"description":"Generated files that were in it: still in your library, now in no project. Nothing but the folder is deleted."}},"required":["id","deleted","generations_kept"],"additionalProperties":false,"description":"A deleted project. Its generated files, uploads and characters are kept."},"ModelVoice":{"type":"object","properties":{"id":{"type":"string","description":"The value to send as `voice`."},"name":{"type":"string"},"lang":{"type":"string","description":"Language, e.g. `English`, `Spanish`, `Chinese (Mandarin)`."},"note":{"type":"string","description":"How the voice sounds, in one line. Empty where there is no description."}},"required":["id","name","lang","note"],"additionalProperties":false,"description":"One voice a text-to-speech model speaks with."},"ModelCaps":{"type":"object","properties":{"durations":{"type":"array","items":{"type":"number"},"description":"Clip lengths in seconds the model accepts. Empty for images and audio."},"duration_editable":{"type":"boolean","description":"False when the model offers exactly one length."},"resolutions":{"type":"array","items":{"type":"string"},"description":"Output resolutions (video) or tiers (image) the model accepts. Empty when fixed."},"default_resolution":{"anyOf":[{"type":"string"},{"type":"null"}]},"ratios":{"type":"array","items":{"type":"string"},"description":"Aspect ratios the model accepts. Empty when the output follows the input."},"qualities":{"type":"array","items":{"type":"string"},"description":"`quality` values, where the model has them."},"modes":{"type":"array","items":{"type":"string"},"description":"`mode` values, where the model has them."},"max_reference_images":{"type":"integer","minimum":0,"description":"Most reference images one input may carry."},"max_reference_videos":{"type":"integer","minimum":0},"max_reference_video_seconds":{"type":"number","minimum":0},"max_reference_audios":{"type":"integer","minimum":0},"max_reference_audio_seconds":{"type":"number","minimum":0},"start_frame":{"type":"boolean","description":"Accepts a first frame (`ref_asset`)."},"end_frame":{"type":"boolean","description":"Accepts a last frame (`last_frame`)."},"audio_input":{"type":"boolean","description":"Accepts an audio file as input."},"audio_generation":{"type":"boolean","description":"Can synthesise a soundtrack (`generate_audio`); usually changes the price."},"audio_always_on":{"type":"boolean","description":"Returns a soundtrack by default, at no extra cost."},"audio_mutable":{"type":"boolean","description":"That default soundtrack can be switched off with `generate_audio: false`."},"negative_prompt":{"type":"boolean"},"seed":{"type":"boolean"},"camera_fixed":{"type":"boolean"},"prompt_extend":{"type":"boolean"},"multi_reference":{"type":"boolean"},"max_prompt_chars":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Longest prompt the model accepts, where it has a limit."},"mutually_exclusive":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"}]},"description":"Pairs of input fields that cannot be sent together, e.g. `[\"ref_asset\",\"ref_images\"]`."},"voice_count":{"type":"integer","minimum":0,"description":"Text to speech: how many voices the model offers — 0 when it has none to choose from. The voices themselves are GET /api/v1/models/{tool}/voices."},"max_speech_chars":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Text to speech: the longest `text` accepted."}},"required":["durations","duration_editable","resolutions","default_resolution","ratios","qualities","modes","max_reference_images","max_reference_videos","max_reference_video_seconds","max_reference_audios","max_reference_audio_seconds","start_frame","end_frame","audio_input","audio_generation","audio_always_on","audio_mutable","negative_prompt","seed","camera_fixed","prompt_extend","multi_reference","max_prompt_chars","mutually_exclusive","voice_count","max_speech_chars"],"additionalProperties":false,"description":"What a model accepts — the same capability table the dispatcher enforces. A value outside it is a 422."},"Model":{"type":"object","properties":{"id":{"type":"string","description":"The value to send as `model`."},"display_name":{"type":"string"},"group":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Quality tier label, e.g. `best`, `quick`."},"tags":{"type":"array","items":{"type":"string"}},"is_default":{"type":"boolean","description":"The model used when a request names none."},"is_new":{"type":"boolean"},"trusted_only":{"type":"boolean","description":"Refused (403 `trusted_required`) unless the key owner is trusted."},"requires_character":{"type":"boolean","description":"Refuses to run without a character's identity sheet as a reference."},"caps":{"$ref":"#/components/schemas/ModelCaps"}},"required":["id","display_name","group","tags","is_default","is_new","trusted_only","requires_character","caps"],"additionalProperties":false,"description":"One enabled model of a tool."},"ModelTool":{"type":"object","properties":{"tool_name":{"type":"string","description":"The value to send as `tool_name`."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"display_name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The catalogue's description (markdown, may be long)."},"default_model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Used when an input names no `model`."},"max_inputs":{"type":"integer","minimum":1,"description":"Most inputs one task of this tool may carry over the API."},"models":{"type":"array","items":{"$ref":"#/components/schemas/Model"},"description":"Enabled models, in catalogue order."}},"required":["tool_name","kind","display_name","description","default_model","max_inputs","models"],"additionalProperties":false,"description":"A tool this deployment sells, with its enabled models."},"ModelToolDetail":{"type":"object","properties":{"tool_name":{"type":"string","description":"The value to send as `tool_name`."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"display_name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The catalogue's description (markdown, may be long)."},"default_model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Used when an input names no `model`."},"max_inputs":{"type":"integer","minimum":1,"description":"Most inputs one task of this tool may carry over the API."},"models":{"type":"array","items":{"$ref":"#/components/schemas/Model"},"description":"Enabled models, in catalogue order."},"input_schema":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"JSON Schema (draft 2020-12) of ONE element of `inputs[]` for this tool. Per-model limits are in each model's `caps`."},"example_input":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"A valid input for the default model, ready to send."}},"required":["tool_name","kind","display_name","description","default_model","max_inputs","models","input_schema","example_input"],"additionalProperties":false,"description":"One tool: its models, their caps, and the input schema."},"ModelSummary":{"type":"object","properties":{"id":{"type":"string","description":"The value to send as `model`."},"name":{"type":"string","description":"Its name, maker included."},"makes":{"type":"string","enum":["image","video","audio"],"description":"What it makes."},"tools":{"type":"array","items":{"type":"string"},"description":"The tools (`tool_name`) that run it."},"default_for":{"description":"The tools it is the default of: the model a request on them runs on when it names none. Absent when none.","type":"array","items":{"type":"string"}},"credits":{"anyOf":[{"type":"object","properties":{"min":{"type":"integer","minimum":0,"description":"The cheapest single image or clip it makes, in credits."},"max":{"type":"integer","minimum":0,"description":"The dearest, at its largest size, longest length and with sound where sound costs extra."}},"required":["min","max"],"additionalProperties":false},{"type":"null"}],"description":"What one image or one clip costs across the settings it offers, in credits. Null where the price follows the length of the text or of a clip you send (speech, upscaling, restyling a clip): price the exact request first."},"resolutions":{"type":"array","items":{"type":"string"},"description":"Resolutions (video) or size tiers (image) it offers. Empty when fixed."},"durations":{"type":"array","items":{"type":"number"},"description":"Clip lengths in seconds. Empty for images and audio."},"ratio_count":{"type":"integer","minimum":0,"description":"How many aspect ratios it accepts; 0 when the output follows the input."},"max_reference_images":{"type":"integer","minimum":0,"description":"Most reference images one request may carry."},"requires_character":{"description":"Present when it refuses to run without a character.","type":"boolean","const":true},"trusted_only":{"description":"Present when only trusted accounts may use it.","type":"boolean","const":true},"voice_count":{"description":"Text to speech: how many voices it has (GET /api/v1/models/{tool}/voices lists them).","type":"integer","minimum":1}},"required":["id","name","makes","tools","credits","resolutions","durations","ratio_count","max_reference_images"],"additionalProperties":false,"description":"One model in a few fields. GET /api/v1/models/{tool} has its full capability table."},"ModelSummaryTool":{"type":"object","properties":{"tool_name":{"type":"string","description":"The value to send as `tool_name`."},"name":{"type":"string"},"makes":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"default_model":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Used when a request names no `model`."},"max_inputs":{"type":"integer","minimum":1,"description":"Most inputs one task of this tool may carry."}},"required":["tool_name","name","makes","default_model","max_inputs"],"additionalProperties":false,"description":"A tool this deployment sells, in a few fields."},"ModelSummaryList":{"type":"object","properties":{"tools":{"type":"array","items":{"$ref":"#/components/schemas/ModelSummaryTool"},"description":"Every tool of the catalogue (filtered by `kind`), on every page."},"data":{"type":"array","items":{"$ref":"#/components/schemas/ModelSummary"},"description":"Models, in catalogue order."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."}},"required":["tools","data","next_cursor"],"additionalProperties":false,"description":"The catalogue in a few fields per model."},"TaskBatch":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Task"},"description":"The tasks asked for that are yours, in the order asked, each exactly as GET /api/v1/tasks/{id} answers it."},"not_found":{"type":"array","items":{"type":"string","format":"uuid","description":"A task id that was asked for."},"description":"The ids that are not yours, deleted, or behind a switch that is off, in the order asked. A batch never fails on one."},"all_finished":{"type":"boolean","description":"True when every task in `data` has a terminal status: nothing in it will change any more."}},"required":["data","not_found","all_finished"],"additionalProperties":false,"description":"Several tasks by id."},"PriceItem":{"type":"object","properties":{"index":{"type":"integer","minimum":0,"description":"The input this line prices, 0-based."},"model":{"type":"string","description":"The model it resolved to."},"unit_price":{"type":"integer","minimum":1,"description":"Credits per unit."},"units":{"type":"integer","minimum":1,"description":"Units: images for image tools (batch size), 1 for video and audio (duration is inside the unit price)."},"total":{"type":"integer","minimum":1,"description":"`unit_price × units` credits — what this input would hold."}},"required":["index","model","unit_price","units","total"],"additionalProperties":false},"Price":{"type":"object","properties":{"tool_name":{"type":"string"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PriceItem"},"description":"One line per input."},"total":{"type":"integer","minimum":0,"description":"Credits the request would hold. You are charged only for what is delivered."},"balance":{"type":"integer","description":"The key owner's spendable credits right now."},"affordable":{"type":"boolean","description":"`balance >= total`. False means the generation would be refused with 402."},"shortfall":{"type":"integer","minimum":0,"description":"Credits missing when not affordable; 0 otherwise."}},"required":["tool_name","items","total","balance","affordable","shortfall"],"additionalProperties":false,"description":"A price for a request, computed exactly as a generation would hold it. Nothing is held."},"Balance":{"type":"object","properties":{"balance":{"type":"integer","description":"Spendable credits. Holds for running tasks are already deducted."},"held":{"type":"integer","minimum":0,"description":"Credits reserved by unfinished tasks."},"allowance":{"type":"integer","minimum":0,"description":"The part of `balance` that expires (a plan allowance). Not an extra bucket."},"allowance_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When `allowance` lapses."},"lifetime_added":{"type":"integer","minimum":0,"description":"Credits ever added."},"lifetime_spent":{"type":"integer","minimum":0,"description":"Credits ever spent."}},"required":["balance","held","allowance","allowance_expires_at","lifetime_added","lifetime_spent"],"additionalProperties":false,"description":"A credit balance. An account with no balance row reads as all zeros."},"ApiKeyLimits":{"type":"object","properties":{"kinds":{"anyOf":[{"type":"array","items":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."}},{"type":"null"}],"description":"The kinds of media this key may generate: some of `image`, `video`, `audio`. Null: every kind. A generation of any other kind is refused with 403 `forbidden`, `meta.reason: \"kind_not_allowed\"`."},"models":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"An allowlist: the only model ids this key may generate with, on any tool. Null: every model (less `blocked_models`). While it is set, a model added to the catalogue later is NOT usable by this key until it is added to the key. Any other model is refused with 403 `forbidden`, `meta.reason: \"model_not_allowed\"`."},"blocked_models":{"type":"array","items":{"type":"string"},"description":"Model ids this key may never generate with, whatever `kinds` and `models` allow — e.g. a model switched off for this key while it is unstable. Empty: none blocked. Refused like `models`."}},"required":["kinds","models","blocked_models"],"additionalProperties":false,"description":"What this key may generate. Set where the key is managed, and effective on the next request. GET /api/v1/models shows only what these allow; the OpenAPI document leaves out /images or /videos for a key that may not make that kind. A request outside them is refused before anything is priced or held."},"Me":{"type":"object","properties":{"api_version":{"type":"string","const":"v1"},"channel":{"type":"string","enum":["api","mcp"],"description":"`api`: a request with an API key."},"key":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The key id, as `tasks.api_key_id` shows it."},"name":{"type":"string"},"prefix":{"type":"string","description":"The key's first 14 characters: the brand prefix, `_live_` and five more. The key itself is never returned."},"scopes":{"type":"array","items":{"type":"string","enum":["read","generate","platform"],"description":"`read` lists and reads the key owner's own account; `generate` creates tasks and uploads, and spends the key owner's credits. Each is granted separately."}},"created_at":{"type":"string","format":"date-time","description":"When the key was issued."},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Last use, updated at most once a minute."},"limits":{"$ref":"#/components/schemas/ApiKeyLimits"},"cap_credits_30d":{"type":"integer","minimum":0,"description":"The most credits this key may spend in any 30 days: the cap you set for it in Settings → API keys."},"spent_credits_30d":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Credits this key has spent in the last 30 days, including credits held for work still running; a refunded call does not count. Null if it could not be read just now."}},"required":["id","name","prefix","scopes","created_at","last_used_at","limits","cap_credits_30d","spent_credits_30d"],"additionalProperties":false},"account":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"The key owner. Every credit and output of this key is theirs."},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"role":{"type":"string","description":"The key owner's profile role, e.g. `user` or `admin`. A key you made in Settings acts only as your own account, whatever the role."},"nsfw_allowed":{"type":"boolean","description":"Whether adult generation is granted to this account on this deployment."},"trusted":{"type":"boolean","description":"Whether trusted-only models are available."}},"required":["user_id","email","role","nsfw_allowed","trusted"],"additionalProperties":false},"balance":{"$ref":"#/components/schemas/Balance"},"limits":{"type":"object","properties":{"requests_per_minute":{"type":"integer"},"requests_per_day":{"type":"integer"},"generations_per_minute":{"type":"integer"},"max_inputs_per_task":{"type":"integer"},"max_images_per_input":{"type":"integer"},"max_in_flight_calls":{"type":"integer","description":"Unfinished calls the account may have at once, website traffic included."},"open_calls":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Unfinished calls right now. Null if they could not be counted."}},"required":["requests_per_minute","requests_per_day","generations_per_minute","max_inputs_per_task","max_images_per_input","max_in_flight_calls","open_calls"],"additionalProperties":false}},"required":["api_version","channel","key","account","balance","limits"],"additionalProperties":false,"description":"Who this key is, what it may spend, and how much room is left."},"LedgerEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The entry id."},"type":{"type":"string","enum":["topup","spend","refund","bonus","grant","expiry"],"description":"`spend` (a hold), `refund`, `topup`, `bonus`, `grant` or `expiry`."},"source":{"type":"string","description":"What caused it, e.g. `task`, `refund`, `stripe`, `adjustment`."},"kind":{"type":"string","enum":["plan_purchase","plan_renewal","plan_upgrade","plan_month","plan_credits_ended","credits_expired","pack_purchase","adjustment","spend","refund","bonus","other"],"description":"What the entry is. `plan_purchase` / `plan_renewal` (a plan period bought by card), `plan_upgrade` (a paid plan change), `plan_month` (a prepaid month of an annual plan), `plan_credits_ended` (unused plan credits at the end of a period), `credits_expired` (expiring credits that lapsed, or that the first period of a plan replaced, such as a signup bonus), `pack_purchase` (a credit pack bought), `adjustment` (made by our staff), `spend`, `refund`, `bonus`, or `other`. Read this rather than `source` to tell a purchase from a correction: a plan period is stored with `source` `adjustment` and `type` `grant`."},"label":{"type":"string","description":"The entry in English, e.g. `Plan purchase · Feature monthly`, `Credit pack`, `Spend`. For display; switch on `kind`."},"amount":{"type":"integer","description":"Signed credits: negative takes from the balance, positive adds to it."},"resulting_amount":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"The balance right after this entry."},"channel":{"type":"string","enum":["web","api","mcp","system"],"description":"Channel recorded on the entry. Refund entries are always `web` or `system` whatever created the task — attribute money to a channel through the task, not this field."},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The task, for holds and refunds."},"call_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The call, for per-call refunds."},"hold_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The hold it belongs to."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why, where recorded, e.g. `call_failed`."},"created_at":{"type":"string","format":"date-time","description":"When it was booked."}},"required":["id","type","source","kind","label","amount","resulting_amount","channel","task_id","call_id","hold_id","reason","created_at"],"additionalProperties":false,"description":"One line of the credit ledger."},"Asset":{"type":"object","properties":{"asset_id":{"type":"string","format":"uuid","description":"The asset id. Pass it to any asset port of a later generation."},"kind":{"type":"string","enum":["image","video","audio"],"description":"`image`, `video` or `audio`."},"mime":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"MIME type of the original file, e.g. `image/png`, `video/mp4`."},"bytes":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Size of the original in bytes, when recorded."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel width, when known. Older uploads may have none."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Pixel height, when known."},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Duration of video or audio in milliseconds, when known."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The original file. A signed URL valid until `expires_at`, or a permanent public URL. Null if the object could not be signed."},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A small preview (webp still, or a short mp4 for video) with the same lifetime as `url`, when one exists."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When `url` and `preview_url` stop working. Null for public URLs, which do not expire."},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short link to the original that opens in a browser until `link_expires_at` (7–8 days), however often it is clicked: each visit is sent on to a freshly signed copy. Give people this, not `url`. Null where this deployment has no links."},"preview_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A still of at most 1024 px (WebP): the picture itself, or a video's still. Null for audio, for a video with no still yet, and for formats that cannot be drawn (HEIC)."},"thumb_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The 320 px still, for grids. Null when `preview_link` is."},"link_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the three links stop working. The same all day: a link minted today is still this link later today."},"source":{"type":"string","enum":["upload","generation","system","import","avatar"],"description":"`upload`, `import`, `generation`, `system` or `avatar`."},"origin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where an imported file was fetched from."},"created_at":{"type":"string","format":"date-time","description":"When the asset row was created."},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When an upload was confirmed. Null for an upload that was never confirmed (unusable, purged after 2 hours)."}},"required":["asset_id","kind","mime","bytes","width","height","duration_ms","url","preview_url","expires_at","link","preview_link","thumb_link","link_expires_at","source","origin_url","created_at","confirmed_at"],"additionalProperties":false,"description":"A stored file you can reference by `asset_id` in any generation."},"AssetsDeleted":{"type":"object","properties":{"deleted":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The uploaded and imported files this call deleted (asset ids, in the order sent): gone from your library, every picker and any project; their short links stop opening, and a URL already handed out lasts until its expires_at."},"already_deleted":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Uploaded and imported files of yours that were already deleted before this call: nothing more to do."}},"required":["deleted","already_deleted"],"additionalProperties":false,"description":"What a delete removed. Sending the same request again is safe: everything comes back in `already_deleted`."},"MediaBatch":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"The files asked for that you may see, in the order asked, each once — with short links and signed URLs."},"not_found":{"type":"array","items":{"type":"string","format":"uuid","description":"An id that was asked for."},"description":"The ids that are not yours, deleted, or behind a switch that is off, in the order asked. A batch never fails on one of them."}},"required":["data","not_found"],"additionalProperties":false,"description":"Several media files by id, as a gallery shows them."},"AssetUpload":{"type":"object","properties":{"asset_id":{"type":"string","format":"uuid","description":"The reserved asset id. Unusable until POST /api/v1/assets/{id}/confirm."},"upload":{"type":"object","properties":{"url":{"type":"string","description":"PUT the raw bytes here — not multipart, not base64."},"method":{"type":"string","const":"PUT"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Send exactly these headers with the PUT (at least `content-type`, matching `mime`)."},"expires_at":{"type":"string","format":"date-time","description":"The upload URL stops working at this time (2 hours). Unconfirmed uploads are purged then."}},"required":["url","method","headers","expires_at"],"additionalProperties":false}},"required":["asset_id","upload"],"additionalProperties":false,"description":"A reserved asset and a signed URL to upload its bytes to. Then confirm it."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"description":"A UUID you generate for this generation, e.g. `crypto.randomUUID()`. Sending the same key again returns the task the first call created instead of charging twice — reuse it when retrying after a timeout; use a new one for a new generation. Keys never expire. The same request sent again answers the original task with `replayed: true`; a key reused for a different request — another prompt, setting, file or count — is a 409 conflict, and nothing is charged.","schema":{"type":"string","format":"uuid"}}},"headers":{"RequestId":{"description":"Unique id of this request; also in every error body as `request_id`.","schema":{"type":"string","format":"uuid"}},"RateLimitLimit":{"description":"The limit of the rule nearest to refusing this key.","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in that rule's current window.","schema":{"type":"integer"}},"RateLimitReset":{"description":"Seconds until that window resets.","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"IdempotencyReplayed":{"description":"`true` when the response replays an earlier request with the same Idempotency-Key.","schema":{"type":"string","enum":["true"]}},"WwwAuthenticate":{"description":"`Bearer realm=\"…\"` on every 401.","schema":{"type":"string"}}}},"x-error-codes":[{"code":"bad_request","status":400},{"code":"invalid_media_type","status":400},{"code":"undecodable_image","status":400},{"code":"unauthenticated","status":401},{"code":"session_fingerprint_mismatch","status":401},{"code":"nsfw_disabled","status":401},{"code":"insufficient_credits","status":402},{"code":"no_payment_method","status":402},{"code":"key_cap_reached","status":402},{"code":"forbidden","status":403},{"code":"account_blocked","status":403},{"code":"trusted_required","status":403},{"code":"tool_restricted","status":403},{"code":"device_limit_exceeded","status":403},{"code":"not_eligible","status":403},{"code":"not_found","status":404},{"code":"conflict","status":409},{"code":"version_locked","status":409},{"code":"open_payments","status":409},{"code":"already_active","status":409},{"code":"payload_too_large","status":413},{"code":"validation_error","status":422},{"code":"rate_limited","status":429},{"code":"internal_error","status":500},{"code":"not_implemented","status":501},{"code":"upstream_error","status":502},{"code":"maintenance","status":503}],"x-rate-limits":[{"group":"apiCustomer","scope":"api-customer","counted_per":"api_key","rules":[{"limit":60,"window_seconds":60},{"limit":2000,"window_seconds":86400}],"applies_to":"Every authenticated request, per API key."},{"group":"apiCustomerGen","scope":"api-customer-gen","counted_per":"api_key","rules":[{"limit":10,"window_seconds":60}],"applies_to":"Requests that create a generation task (POST /generations, /images, /videos, /photoshoots and the character endpoints), per API key."},{"group":"apiCustomerGenAccount","scope":"api-customer-gen-account","counted_per":"account","rules":[{"limit":300,"window_seconds":86400}],"applies_to":"Requests that create a generation task the account has the credits for, counted across every API key of one account together."},{"group":"apiCustomerFleet","scope":"api-customer-fleet","counted_per":"deployment","rules":[{"limit":600,"window_seconds":3600}],"applies_to":"Requests that create a generation task the account has the credits for, counted across every customer’s API keys together."},{"group":"apiCustomerImport","scope":"api-customer-import","counted_per":"api_key","rules":[{"limit":30,"window_seconds":3600}],"applies_to":"POST /assets/import (the server downloads a URL), per API key, whether or not the import succeeds."},{"group":"apiAuthFail","scope":"api-auth-fail","counted_per":"client_ip_hash","rules":[{"limit":30,"window_seconds":300}],"applies_to":"Failed authentications that reach the key table (an unknown or revoked key, or an owner who may not use it), per client network: the IPv4 address, or the /64 of an IPv6 one. A missing or malformed key is refused without being counted."}],"externalDocs":{"description":"The developer guide","url":"https://www.ochra.io/developers"}}