{"openapi":"3.1.0","info":{"title":"Plug-in AI Provider API","version":"2026-09-08","description":"Token-only media generation at normal Plug-in retail prices. Personal access tokens and delegated OAuth bearer tokens use the same membership, client fence, attribution, approval and wallet rules as the website. No integration-specific discount. Generate only after user approval of the work and quoted credits. See /api/public/models for current model capabilities and availability."},"servers":[{"url":"https://plug-in.ai"}],"security":[{"bearerAuth":[]}],"x-billing":{"platform_cost_cents":"integer generation credits, actual retail wallet debit","billable_cost_cents":"agency-to-client invoice currency cents","missing_cost":"null, never inferred from upstream cost or current price"},"x-rate-limits":{"per_token_requests_per_minute":null,"description":"No fixed per-token requests/minute rejection ceiling currently. Token usage is recorded. Paid generation uses per-user/provider admission and wallet/budget reservations; accepted work may queue. Poll responsibly and respect Retry-After when present."},"x-idempotency":{"header":"Idempotency-Key","body_field":"idempotency_key","min_length":1,"max_length":200,"scope":"organization and user","minimum_retention_hours":24,"description":"Printable opaque key, case-sensitive. Header and body must match when both supplied. Accepted identities are not recycled after 24 hours. Retry identical work with the same key. A new key authorizes new work."},"x-completion-delivery":{"webhooks":false,"callback_url_supported":false,"mode":"polling","description":"Poll GET /api/generations/{id}. A 202 queued image/audio admission response may require retrying the identical POST with the same key to resume; this does not authorize duplicate work. Async video runs durably."},"paths":{"/api/public/openapi.json":{"get":{"operationId":"getProviderOpenApi","security":[],"summary":"Public provider contract","responses":{"200":{"description":"OpenAPI 3.1 document; no sign-in required.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/public/models":{"get":{"operationId":"getPublicModels","security":[],"summary":"Current public models and capability metadata","parameters":[{"in":"query","name":"mode","required":false,"schema":{"type":"string","enum":["image","video","audio"]}},{"in":"query","name":"available","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Platform model IDs, capabilities and quote basis. Availability may change; generation is validated again on submission.","content":{"application/json":{"schema":{"type":"object","properties":{"models":{"type":"array","items":{"type":"object"}}}}}}}}}},"/api/clients":{"post":{"operationId":"createClient","summary":"Create or find client","tags":["Attribution"],"description":"Requires admin/creator and delegated mcp:write. external_ref is unique per organization. Replays return 200, unchanged; new records return 201. Reusing a job/brand reference for different client attribution returns 409.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":160},"external_ref":{"type":"string","minLength":1,"maxLength":200,"description":"Organization-scoped, case-sensitive external identity. Replay returns the existing record and never updates it."},"budget_cents":{"type":["integer","null"],"minimum":0,"maximum":2147483647,"description":"Per-client retail generation-credit ceiling; null removes the ceiling. A replay never changes this value."},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"billing_address":{"type":"string"},"tax_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Existing record; no duplicate created.","content":{"application/json":{"schema":{"type":"object","required":["client"],"properties":{"client":{"$ref":"#/components/schemas/AttributionRecord"}}}}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","required":["client"],"properties":{"client":{"$ref":"#/components/schemas/AttributionRecord"}}}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/clients/{id}":{"patch":{"operationId":"updateClientBudget","tags":["Attribution"],"summary":"Update a client's retail-credit ceiling","description":"Administrator only; delegated mcp:write. A client creator cannot change the spending ceiling through this endpoint. Null removes the ceiling. Existing optional client-contact fields remain supported.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"budget_cents":{"type":["integer","null"],"minimum":0,"maximum":2147483647,"description":"Retail generation credits."},"name":{"type":"string"},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"billing_address":{"type":"string"},"tax_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated client.","content":{"application/json":{"schema":{"type":"object","properties":{"client":{"$ref":"#/components/schemas/AttributionRecord"}}}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/jobs":{"post":{"operationId":"createJob","summary":"Create or find job","tags":["Attribution"],"description":"Requires admin/creator and delegated mcp:write. external_ref is unique per organization. Replays return 200, unchanged; new records return 201. Reusing a job/brand reference for different client attribution returns 409.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id","code"],"properties":{"client_id":{"type":"string","format":"uuid"},"code":{"type":"string","minLength":1,"maxLength":40},"name":{"type":"string","maxLength":160},"external_ref":{"type":"string","minLength":1,"maxLength":200,"description":"Organization-scoped, case-sensitive external identity. Replay returns the existing record and never updates it."}}}}}},"responses":{"200":{"description":"Existing record; no duplicate created.","content":{"application/json":{"schema":{"type":"object","required":["job"],"properties":{"job":{"$ref":"#/components/schemas/AttributionRecord"}}}}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","required":["job"],"properties":{"job":{"$ref":"#/components/schemas/AttributionRecord"}}}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/brand-kits":{"post":{"operationId":"createAttributionBrandKit","summary":"Create or find brand_kit","tags":["Attribution"],"description":"Requires admin/creator and delegated mcp:write. external_ref is unique per organization. Replays return 200, unchanged; new records return 201. Reusing a job/brand reference for different client attribution returns 409.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","attribution_only"],"properties":{"name":{"type":"string","minLength":1,"maxLength":160},"client_id":{"type":["string","null"],"format":"uuid"},"external_ref":{"type":"string","minLength":1,"maxLength":200,"description":"Organization-scoped, case-sensitive external identity. Replay returns the existing record and never updates it."},"attribution_only":{"const":true,"description":"Creates an immediately approved attribution kit. This is not creative brand approval or a completed brand interview."},"palette":{"type":"array","maxItems":8,"items":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"tone":{"type":"string","maxLength":240}},"description":"Typography/type is not accepted here; use the brand-file authoring workflow."}}}},"responses":{"200":{"description":"Existing record; no duplicate created.","content":{"application/json":{"schema":{"type":"object","required":["brand_kit"],"properties":{"brand_kit":{"$ref":"#/components/schemas/AttributionRecord"}}}}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","required":["brand_kit"],"properties":{"brand_kit":{"$ref":"#/components/schemas/AttributionRecord"}}}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/uploads/reference":{"post":{"operationId":"createReferenceUpload","tags":["Media"],"summary":"Create a direct media-upload ticket","description":"Requires admin/creator and delegated mcp:write. Send metadata here, then PUT exact bytes to upload_url with required_headers. Native HTTP clients must send every returned header. Browsers derive Content-Length from File. Use returned file_url as the generation reference. URLs expire; keep asset_id and do not log signed URLs or credentials.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content_type","size_bytes"],"properties":{"content_type":{"type":"string","pattern":"^(image|video|audio)/"},"size_bytes":{"type":"integer","minimum":1,"maximum":524288000}}}}}},"responses":{"200":{"description":"Upload ticket. No generation has been requested.","content":{"application/json":{"schema":{"type":"object","required":["asset_id","upload_url","required_headers","file_url"],"properties":{"asset_id":{"type":"string","format":"uuid"},"upload_url":{"type":"string","format":"uri"},"required_headers":{"type":"object","additionalProperties":{"type":"string"}},"file_url":{"type":"string","format":"uri"},"content_type":{"type":"string"}}}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/generations/quote":{"post":{"operationId":"quoteGeneration","tags":["Generation"],"summary":"Quote one to four generations at a binding maximum","description":"Requires admin/creator and delegated mcp:write, like generating. Read-only: nothing is reserved, charged, generated, signed or stored. Each item's maximum_credits is the most the same settings may ever charge on POST /api/generations/sync (the larger of the estimate and what Generate holds while it runs). Send its quote_fingerprint back with quote_version and approved_max_credits to generate exactly what was quoted; if the settings or their price changed in between, nothing starts (409 quote_changed). The same refusals as generating apply before a price is given. Film steps and Reframe are not quoted here yet.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationQuoteRequest"}}}},"responses":{"200":{"description":"A binding quote. Not permission to run: the generation request is checked again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationQuote"}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role (a client account cannot generate), or a plan restriction (plan_required) or an unregistered person (likeness_not_registered) the generation would also refuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"customer_quote_unavailable: no maximum can be confirmed for these settings. voice_batch_approval_unavailable: a voice's first speech must be quoted on its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"body_too_large: the quote request is over 256 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited; honor Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/generations/sync":{"post":{"operationId":"generateMedia","tags":["Generation"],"summary":"Submit one approved generation","description":"Requires admin/creator and delegated mcp:write. The same retail pricing, client budget and wallet admission apply to integrations and website users. For video use prefer_async:true and poll the returned ID. Inspect list_models and estimate_cost over MCP, or POST /api/generations/quote, before approval; supported settings differ by model. With quote_version, quote_fingerprint and approved_max_credits the work starts only at exactly the quoted settings and maximum, and the maximum frozen on the generation names the fingerprint. A binding quote is optional with a personal access token or a partner key: without one, behaviour is unchanged. With an OAuth grant (Claude, ChatGPT or another connected app) it is required: new work without one is refused 428 approval_required before anything is reserved; a replay or resume of accepted work is not. No completion webhook/callback_url support.","parameters":[{"in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string","minLength":1,"maxLength":200},"description":"Stable key for this approved work. Same key with changed work returns 409."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRequest"}}}},"responses":{"201":{"description":"Synchronous completion. Output URLs use the protected platform media gateway. Cost is generation credits; charged:false means debit confirmation is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncResult"}}},"headers":{"Idempotency-Replayed":{"schema":{"type":"string","enum":["true"]},"description":"Present when a previous accepted request is replayed."}}},"202":{"description":"Accepted or replayed durable job/admission wait. Persist generation_id; this is not proof that an output is already ready.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsyncResult"}}}},"400":{"description":"Invalid fields or unsupported model settings. With a binding quote: invalid_quote_version or invalid_quote_fingerprint (send both, as quoted), approval_required (send approved_max_credits with them).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"insufficient_credits for wallet funds; client_budget_exhausted for this client's ceiling; other scoped budget limits may also refuse work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. approved_price_exceeded: the current maximum is above approved_max_credits (credits names it). quote_changed: the request no longer matches the quote's fingerprint or maximum; quote again. Nothing started and nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Unsupported inputs or content declined. Read error; do not automatically alter and re-spend.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"428":{"description":"approval_required: this caller (an OAuth grant) must send a binding quote with new work. Quote it with POST /api/generations/quote, show its maximum_credits, and send quote_version, quote_fingerprint and approved_max_credits. Nothing started and nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Capacity/rate-related refusal where present; honor Retry-After. A model_busy error may use another status; read its code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Outcome not confirmed. Recover the same key or generation ID before requesting new work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/generations/{id}":{"get":{"operationId":"getGeneration","tags":["Generation"],"summary":"Read status, provenance, attribution and settled costs","description":"Requires delegated mcp:read and the caller's workspace/client scope. Pending or unknown platform cost is null. Completion does not necessarily mean settlement has finished; re-read until platform_cost_cents is known. No supplier identity or upstream cost is exposed.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Generation status and public receipt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRead"}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"History-deleted generation. Minimal scoped receipt remains available; outputs are empty and no deleted media/prompt is exposed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRead"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/generations":{"get":{"operationId":"listGenerationsOrCostStatement","tags":["Generation"],"summary":"History or a full-filter cost statement","description":"Requires delegated mcp:read. Supplying from/to or statement=true activates minimal financial statements, including soft-deleted records. job_id alone retains ordinary gallery semantics. Totals use the entire authorized filtered set, ignoring cursor/limit, and a consistent database snapshot. Date range filters generation created_at, not settlement date; from is inclusive and to exclusive. Invoice cents and retail credits are separate totals. No FX conversion, refunds or manual adjustments are included.","parameters":[{"in":"query","name":"job_id","required":false,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"client_id","required":false,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"from","required":false,"schema":{"type":"string"},"description":"YYYY-MM-DD UTC midnight or ISO 8601 timestamp with timezone; inclusive."},{"in":"query","name":"to","required":false,"schema":{"type":"string"},"description":"YYYY-MM-DD UTC midnight or ISO 8601 timestamp with timezone; exclusive."},{"in":"query","name":"statement","required":false,"schema":{"type":"boolean","default":false}},{"in":"query","name":"limit","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Statement maximum 100; ordinary gallery maximum 60."},{"in":"query","name":"before_id","required":false,"schema":{"type":"string","format":"uuid"},"description":"next_cursor from the preceding page; does not reduce totals."},{"in":"query","name":"ids","required":false,"schema":{"type":"string"},"description":"Comma-separated generation UUIDs, maximum 60."},{"in":"query","name":"modality","required":false,"schema":{"type":"string","enum":["image","video","audio","3d","vector"]}},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["queued","running","completed","failed","canceled"]}}],"responses":{"200":{"description":"Rows and next_cursor; totals/statement metadata appear in statement mode. costs_complete=false signals pending/unknown charges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationList"}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/media/{generationId}":{"get":{"operationId":"getGenerationMedia","tags":["Media"],"summary":"Read authorized output bytes","description":"Use the same bearer credential; links alone do not grant access. Output i uses the ready output order returned by generation read. Video Range requests are supported. Never replace this URL with an upstream supplier URL.","parameters":[{"in":"path","name":"generationId","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"i","required":false,"schema":{"type":"integer","minimum":0,"default":0}},{"in":"header","name":"Range","schema":{"type":"string","example":"bytes=0-1048575"}}],"responses":{"200":{"description":"Authorized media bytes.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"206":{"description":"Requested byte range; Content-Range describes the segment.","headers":{"Content-Range":{"schema":{"type":"string"}}}},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Deleted result; media is no longer available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/mcp":{"post":{"operationId":"mcpRpc","tags":["MCP"],"summary":"Stateless MCP JSON-RPC","description":"Bearer-token interface. tools/list advertises role-allowed tools and live schemas. tools/call includes read tools list_models, estimate_cost, get_balance, list_voices, get_generation; write tools upload_reference, create_client, create_job, create_brand_kit and generation tools. Client viewers receive only their explicit scoped read allowlist. Generation tools spend credits and require user approval. Arabic dialect hints do not translate a script or guarantee an accent without a matching voice.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","number","null"]},"method":{"type":"string","examples":["tools/list","tools/call"]},"params":{"type":"object","properties":{"name":{"type":"string","examples":["estimate_cost"]},"arguments":{"type":"object"}}}}}}}},"responses":{"200":{"description":"JSON-RPC result/error. Tool results may contain isError:true; inspect it even on HTTP 200.","content":{"application/json":{"schema":{"type":"object","properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","number","null"]},"result":{"type":"object"},"error":{"type":"object"}}}}}},"202":{"description":"Notification acknowledged without a result body."},"400":{"description":"Invalid fields, unsupported model settings or invalid idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient delegated scope/role, unavailable workspace, or plan restriction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Record unavailable within this caller's workspace/client scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency, attribution or request-state conflict. Do not change approved work under the same key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable or submission outcome not confirmed. Recover using the same key/ID; never silently make a new paid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Personal access token (pk_live_…) from Account/API keys or delegated OAuth access token (oat_live_…). OAuth requires mcp:read for reads and mcp:write for mutations. Personal tokens inherit current membership; client fencing applies to both."}},"schemas":{"Attribution":{"type":"object","required":["client_id","job_id","brand_kit_id"],"properties":{"client_id":{"type":["string","null"],"format":"uuid"},"job_id":{"type":["string","null"],"format":"uuid"},"brand_kit_id":{"type":["string","null"],"format":"uuid"}}},"AttributionRecord":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"externalRef":{"type":["string","null"]},"clientId":{"type":["string","null"],"format":"uuid"},"budgetCents":{"type":["integer","null"]},"approvalStatus":{"type":"string"}},"description":"Create-route record fields are camelCase; request attribution fields use snake_case."},"Receipt":{"type":"object","properties":{"attribution":{"$ref":"#/components/schemas/Attribution"},"provenance_id":{"type":"string","description":"Stable urn:plug-in:generation:<generation UUID>, unchanged by polling or replay."},"platform_cost_cents":{"type":["integer","null"],"minimum":0,"description":"Integer generation credits despite the legacy _cents suffix. Null means a settled debit is not yet known. Not USD or AED cash cents."},"billable_cost_cents":{"type":["integer","null"],"minimum":0,"description":"Stored agency-to-client invoice amount in invoice currency cents. Separate from the retail wallet charge; not upstream supplier cost."}}},"Output":{"type":"object","properties":{"asset_id":{"type":"string","format":"uuid"},"url":{"type":"string","description":"Protected relative /api/media/<generationId>?i=N URL."},"mime_type":{"type":"string"},"size_bytes":{"type":["integer","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"duration_sec":{"type":["string","number","null"]}}},"GenerationRead":{"allOf":[{"$ref":"#/components/schemas/Receipt"},{"type":"object","required":["id","status","model_id"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","running","completed","failed","canceled"]},"model_id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/Output"}},"deleted":{"type":"boolean"},"error_code":{"type":["string","null"]},"error_message":{"type":["string","null"]},"raw_cost_cents":{"type":["integer","null"],"minimum":0,"description":"Legacy alias for the actual retail wallet debit; never upstream supplier cost. Prefer platform_cost_cents."}}}]},"SyncResult":{"allOf":[{"$ref":"#/components/schemas/Receipt"},{"type":"object","required":["generation_id","status"],"properties":{"generation_id":{"type":"string","format":"uuid"},"status":{"const":"completed"},"urls":{"type":"array","items":{"type":"string"}},"charged":{"type":"boolean"},"billing_status":{"type":"string","enum":["settled","pending","client_budget_exhausted"],"description":"Delivered output may precede settlement. A client-budget refusal here preserves the output and unbilled obligation; platform_cost_cents is null. Raise the ceiling and reconcile, never generate again to recover payment."},"credit_balance_cents":{"type":["integer","null"],"minimum":0,"description":"Integer generation credits despite the legacy _cents suffix. Null means a settled debit is not yet known. Not USD or AED cash cents."}}}]},"AsyncResult":{"allOf":[{"$ref":"#/components/schemas/Receipt"},{"type":"object","required":["generation_id","status"],"properties":{"generation_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","running","completed","failed","canceled"]},"queued":{"type":"boolean"},"estimated_cost_cents":{"type":["integer","null"],"minimum":0,"description":"Integer generation credits despite the legacy _cents suffix. Null means a settled debit is not yet known. Not USD or AED cash cents."}}}]},"GenerationRequest":{"type":"object","required":["model_id","output_type","client_id","job_id","brand_kit_id"],"properties":{"model_id":{"type":"string","description":"Canonical platform model ID from list_models; no supplier endpoint ID."},"output_type":{"type":"string","enum":["image","video","audio"]},"prompt":{"type":"string","description":"User script/prompt. Arabic is preserved by default; model-specific limits and reference-only exceptions apply."},"client_id":{"type":"string","format":"uuid"},"job_id":{"type":"string","format":"uuid"},"brand_kit_id":{"type":"string","format":"uuid"},"idempotency_key":{"type":"string","minLength":1,"maxLength":200},"request_id":{"type":"string","format":"uuid","description":"Legacy UUID request identity; do not change it on retry. Prefer one stable idempotency contract per logical request."},"prefer_async":{"type":"boolean","description":"Use true for video; persist returned generation_id and poll."},"resume_generation_id":{"type":"string","format":"uuid","description":"Resume a known queued generation with identical settings and key; never a new request."},"approved_max_credits":{"type":"integer","minimum":1,"description":"Optional. The most the person approved, in generation credits. If the current maximum is higher, nothing starts (409 approved_price_exceeded). Required with a binding quote, where it must equal the quote item's maximum_credits."},"quote_version":{"const":"generation-quote-v1","description":"Optional with a personal access token or a partner key; required on new work with an OAuth grant (428 approval_required). Send with quote_fingerprint and approved_max_credits to generate exactly what POST /api/generations/quote quoted."},"quote_fingerprint":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Optional with a personal access token or a partner key; required on new work with an OAuth grant. The quote item's quote_fingerprint, as given. On new work the request is prepared again and must come out at this fingerprint and approved_max_credits, or nothing starts (409 quote_changed). A replay or resume of an accepted request keeps its original path."},"likeness_consent":{"type":"boolean","description":"True only after the user explicitly declares the realistic people in these pictures. Requires all likeness declaration and terms fields; no kind or terms acceptance is inferred."},"likeness_kind":{"type":"string","enum":["real","ai_made"],"description":"Explicit user choice: real means written permission, plus a verified registered likeness for Seedance; ai_made means invented, nobody real and not generated from a real person's photo. No default. Approval is not guaranteed."},"likeness_version":{"const":"2026-10-18","description":"Portrait declaration version the user accepted."},"likeness_terms_accepted":{"type":"boolean","description":"True only after explicit acceptance of /terms for this use, before portrait submission."},"likeness_terms_version":{"const":"2026-10-02","description":"Published /terms version accepted for this use."},"aspect":{"type":"string","examples":["16:9","9:16","1:1"]},"quality":{"type":"string","examples":["1K","2K","4K"],"description":"Image model quality tier; not video resolution."},"render_quality":{"type":"string","enum":["very_low","low","medium","high","xhigh","max"],"description":"Images, on models that sell a rendering quality (list_models capabilities.renderQualities, with each model's own levels: ideogram_4_5_edit very_low–high; gpt_image_2 low–high, gpt_image_2_5 low–max; grok_image low–medium): how much detail is drawn, never the output size, which `quality` still sets where the model has both. Each level has its own credit price; omitted means the model's default (capabilities.renderQualityDefault). Refused with 400 invalid_render_quality on any other model or value, before anything is charged."},"count":{"type":"integer","minimum":1,"maximum":3,"default":1,"description":"Images: 1–3 if supported. Video/audio: exactly 1; separate deliberately approved requests for variations."},"seed":{"type":"integer","description":"Only when supported by the selected model."},"duration_seconds":{"type":"number","description":"Model-specific duration. A video is rendered, reserved, charged and recorded at the length its model really makes: Kling 2.6 makes 5 or 10 s, Kling 3.0 3–15, MiniMax-H3 4 (5 at 480p) to 15, Grok 1–15, so another value is moved to the nearest one the model takes before anything is charged. Gemini Omni chooses its own length (3–10 s): it is quoted at 10 s and charged on the clip it returns. Seed Audio uses approximate target_duration_seconds instead of a native fixed-length request."},"resolution":{"type":"string","examples":["720p","1080p"],"description":"Supported video resolution. Do not infer 4K from image quality; Seedance 2.5 is validated against its actual catalogue."},"image_url":{"type":"string","description":"Authorized source image/start frame."},"reference_image_urls":{"type":"array","items":{"type":"string"}},"reference_video_urls":{"type":"array","items":{"type":"string"}},"reference_audio_urls":{"type":"array","items":{"type":"string"}},"voice_id":{"type":"string","description":"Get compatible IDs from list_voices with the same model_id. Preserve lib: prefix for saved voices; Seed voice is optional."},"source_media_url":{"type":"string","description":"Authorized existing recording for clone/voicechange/dub models."},"voice_name":{"type":"string"},"target_language":{"type":"string"},"language":{"type":"string","enum":["ar","ar-AE","ar-SA","ar-EG","ar-LB","ar-MA"],"description":"Arabic speech hint on supported ElevenLabs/MiniMax paths; see live tool schema. Does not translate or inject a different script."},"dialect":{"type":"string","enum":["ar","ar-AE","ar-SA","ar-EG","ar-LB","ar-MA"],"description":"Accent preference depends on a matching selected voice's declared metadata; not a guarantee or automatic voice substitution."},"arabic_assist":{"type":"boolean","default":false,"description":"Explicit opt-in English prompt adaptation for image/video. Without it, the platform does not translate Arabic prompts."},"target_duration_seconds":{"type":"integer","minimum":5,"maximum":120,"description":"Optional approximate Seed Audio prompt timing; omit with an image reference. Full reservation is unchanged."}},"allOf":[{"if":{"properties":{"output_type":{"enum":["video","audio"]}},"required":["output_type"]},"then":{"properties":{"count":{"const":1}}}}],"additionalProperties":true},"GenerationQuoteRequest":{"type":"object","required":["operation","draft_revision","items"],"properties":{"operation":{"const":"generation","description":"Only generations are quoted here today."},"draft_revision":{"type":"string","minLength":1,"maxLength":200,"description":"Your own label for the draft being priced; echoed back for correlation, never authorization."},"items":{"type":"array","minItems":1,"maxItems":4,"items":{"type":"object","required":["item_key","input"],"properties":{"item_key":{"type":"string","minLength":1,"maxLength":200,"description":"Unique within the request."},"input":{"$ref":"#/components/schemas/GenerationRequest","description":"The generation body exactly as it will be sent to POST /api/generations/sync. Transport and approval fields (request_id, idempotency_key, approved_max_credits, quote_*) are ignored."}}}}},"description":"At most 256 KB."},"GenerationQuoteItem":{"type":"object","required":["item_key","quote_fingerprint","maximum_credits","settings","components","assumptions"],"properties":{"item_key":{"type":"string"},"quote_fingerprint":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Binds the maximum to these settings, references, client, job and caller. Send it back unchanged."},"maximum_credits":{"type":"integer","minimum":1,"description":"The most this generation may ever charge, in generation credits: pass it as approved_max_credits."},"settings":{"type":"object","description":"The settings the generation runs at once normalized: model_id, output_type, count, aspect, quality, render_quality, resolution, duration_seconds, input_duration_seconds, reference_count."},"components":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["generation","first_voice_use"]},"maximum_credits":{"type":"integer"}}},"description":"The parts of maximum_credits; a designed or cloned voice's first speech carries its one-off fee."},"assumptions":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","examples":["input_duration_measured","input_duration_declared","input_duration_maximum","duration_maximum","outputs_maximum","reserve_above_estimate","first_voice_use","draft_final"]}}},"description":"Why the maximum is what it is, as codes for clients to word."}}},"GenerationQuote":{"type":"object","required":["version","preparation_version","tariff_version","draft_revision","quote_fingerprint","total_maximum_credits","items"],"properties":{"version":{"const":"generation-quote-v1"},"preparation_version":{"type":"string"},"tariff_version":{"type":"string","description":"Opaque digest of the price tables the quote was made with."},"draft_revision":{"type":"string"},"quote_fingerprint":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"The whole quote's fingerprint, for correlation. Each item is confirmed with its own."},"total_maximum_credits":{"type":"integer","minimum":1},"items":{"type":"array","items":{"$ref":"#/components/schemas/GenerationQuoteItem"}}}},"GenerationList":{"type":"object","required":["generations","next_cursor"],"properties":{"generations":{"type":"array","items":{"$ref":"#/components/schemas/GenerationRead"}},"next_cursor":{"type":["string","null"],"format":"uuid"},"statement":{"type":"object"},"filters":{"type":"object"},"totals":{"type":"object","description":"Sums of known integer amounts across the full filtered set. Not just this page. Check unknown/pending counts and costs_complete before final reconciliation.","properties":{"generation_count":{"type":"integer"},"completed_count":{"type":"integer"},"pending_count":{"type":"integer"},"deleted_count":{"type":"integer"},"platform_cost_cents":{"type":"integer","minimum":0},"platform_cost_record_count":{"type":"integer"},"platform_cost_unknown_count":{"type":"integer"},"billable_cost_cents":{"type":"integer","minimum":0},"billable_cost_unknown_count":{"type":"integer"},"costs_complete":{"type":"boolean"}}}}},"Error":{"type":"object","properties":{"error":{"type":"string","examples":["insufficient_credits","client_budget_exhausted","plan_required","org_suspended","org_archived","content_declined","real_person_declined","model_busy","invalid_idempotency_key","idempotency_conflict","request_conflict","external_ref_conflict","invalid_statement_filter","approved_price_exceeded","quote_changed","approval_required","customer_quote_unavailable"]},"message":{"type":"string"},"budget_cents":{"type":"integer","description":"Client's retail-credit ceiling."},"spent_cents":{"type":"integer"},"in_flight_cents":{"type":"integer"},"required_cents":{"type":"integer"}},"description":"Codes are machine-readable. Not every model returns every code; inspect error even when an MCP tools/call returns HTTP 200."}}}}