{"openapi":"3.1.0","info":{"title":"Funnelful API","version":"1.0.0","description":"Generate with one model directly (Models), or drive the Funnelful studio agent (Agent). Authenticate with an account API key from the studio's MCP/API tab, the same keys MCP uses. Everything bills the account's credits at list price, exactly like the web app. Errors are { error: { code, message } }."},"servers":[{"url":"/api/v1"}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"tags":[{"name":"Models","description":"Direct generation, the same as the web app's model playground."},{"name":"Agent","description":"Sessions with the studio agent, the same as the web chat and MCP."}],"paths":{"/models":{"get":{"tags":["Models"],"summary":"List models","operationId":"listModels","responses":{"200":{"description":"Every model, with options, availability and from-price.","content":{"application/json":{"schema":{"type":"object","required":["models","catalogVersion"],"properties":{"models":{"type":"array","items":{"$ref":"#/components/schemas/PlaygroundModelListing"}},"catalogVersion":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/quote":{"post":{"tags":["Models"],"summary":"Quote a request","description":"The list price in USD a generation would cost; nothing is started or billed.","operationId":"quote","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["request"],"properties":{"request":{"$ref":"#/components/schemas/PlaygroundRequest"}}}}}},"responses":{"200":{"description":"The quote.","content":{"application/json":{"schema":{"type":"object","required":["usd","catalogVersion"],"properties":{"usd":{"type":["number","null"],"description":"null when not list-priced."},"catalogVersion":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/Error","description":"Invalid request."},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/generations":{"post":{"tags":["Models"],"summary":"Start a generation","description":"Bills the list price to the account's credits. Poll GET /generations/{id}?wait= until terminal.","operationId":"createGeneration","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["request"],"properties":{"request":{"$ref":"#/components/schemas/PlaygroundRequest"}}}}}},"responses":{"202":{"description":"Started.","content":{"application/json":{"schema":{"type":"object","required":["generation"],"properties":{"generation":{"$ref":"#/components/schemas/PlaygroundGeneration"}}}}}},"400":{"$ref":"#/components/responses/Error","description":"Invalid request."},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"402":{"$ref":"#/components/responses/Error","description":"Not enough credits."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}},"get":{"tags":["Models"],"summary":"List generations","description":"The key user's playground generations, newest first.","operationId":"listGenerations","parameters":[{"name":"limit","in":"query","required":false,"description":"How many to return.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"before","in":"query","required":false,"description":"Only generations created before this time (ISO 8601), for paging.","schema":{"type":"string"}}],"responses":{"200":{"description":"Generations.","content":{"application/json":{"schema":{"type":"object","required":["generations"],"properties":{"generations":{"type":"array","items":{"$ref":"#/components/schemas/PlaygroundGeneration"}}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/generations/{id}":{"get":{"tags":["Models"],"summary":"Get a generation","operationId":"getGeneration","parameters":[{"name":"id","in":"path","required":true,"description":"gen:<uuid>","schema":{"type":"string"}},{"name":"wait","in":"query","required":false,"description":"Seconds to long-poll until it succeeds or fails (0-60, default 0).","schema":{"type":"integer","minimum":0,"maximum":60}}],"responses":{"200":{"description":"The generation.","content":{"application/json":{"schema":{"type":"object","required":["generation"],"properties":{"generation":{"$ref":"#/components/schemas/PlaygroundGeneration"}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown generation."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/uploads":{"post":{"tags":["Models"],"summary":"Upload an image","description":"Upload a file (multipart) or have the server download a public https URL (JSON). Use the returned asset id in references, firstFrame or message attachments.","operationId":"createUpload","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}},"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}}}}},"responses":{"201":{"description":"Uploaded.","content":{"application/json":{"schema":{"type":"object","required":["upload"],"properties":{"upload":{"$ref":"#/components/schemas/Upload"}}}}}},"400":{"$ref":"#/components/responses/Error","description":"Not an accepted file, too large, or the URL could not be fetched."},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/chat-models":{"get":{"tags":["Agent"],"summary":"List chat models","description":"The models the studio agent can run a session on, the default first, with the price per 1M tokens in USD (input, output) charged to the account's credits for the agent's own thinking. Generations the agent starts cost their list price whichever model runs.","operationId":"listChatModels","responses":{"200":{"description":"Chat models.","content":{"application/json":{"schema":{"type":"object","required":["default","models"],"properties":{"default":{"type":"string"},"models":{"type":"array","items":{"$ref":"#/components/schemas/ChatModel"}}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/sessions":{"post":{"tags":["Agent"],"summary":"Start a session","description":"A new studio-agent session (one session = one ad project). Not listed in the web app's Chats.","operationId":"createSession","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"title":{"type":"string"},"chatModel":{"type":"string","description":"The model the studio agent runs the session on: an id from GET /chat-models (\"standard\" is the default). Stays set until changed."}}}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","required":["session"],"properties":{"session":{"$ref":"#/components/schemas/Session"}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}},"get":{"tags":["Agent"],"summary":"List sessions","description":"Sessions started over the API or MCP, newest first.","operationId":"listSessions","parameters":[{"name":"limit","in":"query","required":false,"description":"How many to return.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Sessions.","content":{"application/json":{"schema":{"type":"object","required":["sessions"],"properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/Session"}}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/sessions/{id}":{"get":{"tags":["Agent"],"summary":"Get a session","operationId":"getSession","parameters":[{"name":"id","in":"path","required":true,"description":"The session id (a uuid).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The session.","content":{"application/json":{"schema":{"type":"object","required":["session"],"properties":{"session":{"$ref":"#/components/schemas/Session"}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown session."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/sessions/{id}/messages":{"get":{"tags":["Agent"],"summary":"List messages","operationId":"listMessages","parameters":[{"name":"id","in":"path","required":true,"description":"The session id (a uuid).","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many recent messages to return.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Messages, oldest first.","content":{"application/json":{"schema":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/ApiMessage"}}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown session."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}},"post":{"tags":["Agent"],"summary":"Send a message","description":"Runs one studio-agent turn. With stream=true (default) the response is the turn as server-sent events, ending after turn.completed, turn.failed or turn.stopped. With stream=false it waits up to `wait` seconds and answers JSON (status=running when the turn outlives it: follow it on GET /sessions/{id}/events, never resend). When the agent asks a question, answer it with the next message.","operationId":"sendMessage","parameters":[{"name":"id","in":"path","required":true,"description":"The session id (a uuid).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string","description":"What to tell the agent (a brief, an answer, 'continue')."},"attachments":{"type":"array","items":{"type":"string","description":"asset:<uuid> or gen:<uuid>"}},"chatModel":{"type":"string","description":"The model the studio agent runs the session on: an id from GET /chat-models (\"standard\" is the default). Stays set until changed. Switches from this message on."},"stream":{"type":"boolean","default":true},"wait":{"type":"integer","minimum":0,"maximum":300,"default":120,"description":"stream=false only."}}}}}},"responses":{"200":{"description":"The turn: an event stream (stream=true) or the turn result (stream=false).","content":{"text/event-stream":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Event_turn_started"},{"$ref":"#/components/schemas/Event_message_delta"},{"$ref":"#/components/schemas/Event_tool_started"},{"$ref":"#/components/schemas/Event_tool_completed"},{"$ref":"#/components/schemas/Event_generation_created"},{"$ref":"#/components/schemas/Event_generation_updated"},{"$ref":"#/components/schemas/Event_turn_completed"},{"$ref":"#/components/schemas/Event_turn_failed"},{"$ref":"#/components/schemas/Event_turn_stopped"}]}},"application/json":{"schema":{"$ref":"#/components/schemas/TurnResult"}}}},"400":{"$ref":"#/components/responses/Error","description":"Invalid request."},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"402":{"$ref":"#/components/responses/Error","description":"Not enough credits."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown session."},"409":{"$ref":"#/components/responses/Error","description":"A turn is already running in this session."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/sessions/{id}/stop":{"post":{"tags":["Agent"],"summary":"Stop the running turn","description":"Renders already started keep going; nothing new starts.","operationId":"stopSession","parameters":[{"name":"id","in":"path","required":true,"description":"The session id (a uuid).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Whether a turn was stopped.","content":{"application/json":{"schema":{"type":"object","required":["stopped"],"properties":{"stopped":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown session."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/sessions/{id}/generations":{"get":{"tags":["Agent"],"summary":"List the session's media","description":"Stills, video takes, songs and final cuts made in the session, with presigned URLs.","operationId":"listSessionGenerations","parameters":[{"name":"id","in":"path","required":true,"description":"The session id (a uuid).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Media.","content":{"application/json":{"schema":{"type":"object","required":["generations"],"properties":{"generations":{"type":"array","items":{"$ref":"#/components/schemas/PlaygroundGeneration"}}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown session."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/sessions/{id}/events":{"get":{"tags":["Agent"],"summary":"Follow the session","description":"A server-sent event stream that stays open: replays the running turn's remaining events and emits generation.updated whenever a session generation changes status (renders finish long after the turn). Heartbeat comment every 15 s; closes after 30 min idle or when the client disconnects.","operationId":"sessionEvents","parameters":[{"name":"id","in":"path","required":true,"description":"The session id (a uuid).","schema":{"type":"string","format":"uuid"}},{"name":"after","in":"query","required":false,"description":"Resume after this event id (same as the Last-Event-ID header).","schema":{"type":"string"}},{"name":"Last-Event-ID","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The session's events.","content":{"text/event-stream":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Event_turn_started"},{"$ref":"#/components/schemas/Event_message_delta"},{"$ref":"#/components/schemas/Event_tool_started"},{"$ref":"#/components/schemas/Event_tool_completed"},{"$ref":"#/components/schemas/Event_generation_created"},{"$ref":"#/components/schemas/Event_generation_updated"},{"$ref":"#/components/schemas/Event_turn_completed"},{"$ref":"#/components/schemas/Event_turn_failed"},{"$ref":"#/components/schemas/Event_turn_stopped"}]}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown session."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}},"/voices/{voiceId}/consent":{"post":{"tags":["Agent"],"summary":"Confirm a voice","description":"Confirms the account may use a character voice the agent took from an upload or a researched video (their own voice, or one they have the speaker's permission to use). Until then the agent can't save a character with it. Idempotent.","operationId":"confirmVoice","parameters":[{"name":"voiceId","in":"path","required":true,"description":"voice:<uuid> or the bare uuid.","schema":{"type":"string"}}],"responses":{"200":{"description":"The voice, consented.","content":{"application/json":{"schema":{"type":"object","required":["voice"],"properties":{"voice":{"$ref":"#/components/schemas/Voice"}}}}}},"401":{"$ref":"#/components/responses/Error","description":"Missing or invalid API key."},"403":{"$ref":"#/components/responses/Error","description":"An active subscription is required."},"404":{"$ref":"#/components/responses/Error","description":"Unknown voice."},"500":{"$ref":"#/components/responses/Error","description":"Something went wrong on our side."},"503":{"$ref":"#/components/responses/Error","description":"Storage, the model or its provider is unavailable."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Authorization: Bearer ffk_..."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key"}},"responses":{"Error":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthorized","subscription_required","invalid_request","not_found","conflict","insufficient_credits","unavailable","internal"]},"message":{"type":"string","description":"Customer-safe explanation."}}}}},"Price":{"type":"object","required":["unit","fromUsd"],"properties":{"unit":{"type":"string","enum":["second","image","generation"]},"fromUsd":{"type":"number"}}},"PlaygroundModelListing":{"description":"A model you can generate with directly. Curated models (source \"curated\") list their options; every other wrapped image/video model is \"apimart:<id>\" (source \"apimart\") with its own input contract in parameters.","type":"object","required":["id","label","kind","group","source","price","available"],"properties":{"id":{"type":"string","description":"seedance-2.5, gpt-image-2.5, nano-banana-2, soul-2, suno-v6 or \"apimart:<id>\"."},"label":{"type":"string"},"kind":{"type":"string","enum":["video","image","song","audio"]},"group":{"type":"string","enum":["image","video","audio"]},"source":{"type":"string","enum":["curated","apimart"]},"description":{"type":"string","description":"Curated models only."},"modes":{"type":"array","items":{"type":"string","enum":["text","first-frame","reference"]}},"aspectRatios":{"type":"array","items":{"type":"string"}},"resolutions":{"type":"array","items":{"type":"string"}},"defaultResolution":{"type":"string"},"durationSeconds":{"type":"object","required":["min","max","default"],"properties":{"min":{"type":"number"},"max":{"type":"number"},"default":{"type":"number"}}},"maxReferenceImages":{"type":"integer"},"price":{"$ref":"#/components/schemas/Price"},"available":{"type":"boolean"},"unavailableReason":{"type":"string"},"parameters":{"type":"object","description":"\"apimart:<id>\" models: the JSON Schema of params."}}},"PlaygroundRequest":{"description":"What to generate. Missing options take the model's defaults; anything the model does not take is refused (400 invalid_request).","oneOf":[{"$ref":"#/components/schemas/SeedanceRequest"},{"$ref":"#/components/schemas/ImageRequest"},{"$ref":"#/components/schemas/SongRequest"},{"$ref":"#/components/schemas/ApimartRequest"}],"discriminator":{"propertyName":"model"}},"SeedanceRequest":{"type":"object","required":["model","prompt"],"properties":{"model":{"const":"seedance-2.5"},"prompt":{"type":"string"},"mode":{"type":"string","enum":["text","first-frame","reference"],"description":"Default from the inputs."},"durationSeconds":{"type":"integer"},"resolution":{"type":"string"},"aspectRatio":{"type":"string","description":"e.g. 9:16"},"firstFrame":{"type":"string","description":"first-frame mode: the image the video starts on. An image id: asset:<uuid> (an upload) or gen:<uuid> (an earlier image result)."},"references":{"type":"array","items":{"type":"string","description":"An image id: asset:<uuid> (an upload) or gen:<uuid> (an earlier image result)."}}}},"ImageRequest":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string","enum":["gpt-image-2.5","nano-banana-2","soul-2"]},"prompt":{"type":"string"},"resolution":{"type":"string"},"aspectRatio":{"type":"string"},"references":{"type":"array","items":{"type":"string","description":"An image id: asset:<uuid> (an upload) or gen:<uuid> (an earlier image result)."}}}},"SongRequest":{"type":"object","required":["model","style"],"properties":{"model":{"const":"suno-v6"},"prompt":{"type":"string","description":"The lyrics, or a description of the music when instrumental."},"style":{"type":"string","description":"Genre, tempo, mood, instruments, voice."},"title":{"type":"string"},"instrumental":{"type":"boolean"}}},"ApimartRequest":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string","pattern":"^apimart:","description":"An \"apimart:<id>\" model from GET /models."},"prompt":{"type":"string"},"params":{"type":"object","description":"The model's own options (its listing's parameters). Per-second models need duration."},"references":{"type":"array","items":{"type":"string","description":"An image id: asset:<uuid> (an upload) or gen:<uuid> (an earlier image result)."}}}},"PlaygroundGeneration":{"type":"object","required":["id","model","label","kind","status","prompt","params","createdAt","url","thumbUrl","error","priceUsd"],"properties":{"id":{"type":"string","description":"gen:<uuid>"},"model":{"type":"string"},"label":{"type":"string","description":"The model's name."},"kind":{"type":"string","enum":["video","image","song","audio"]},"status":{"type":"string","enum":["queued","running","succeeded","failed"]},"prompt":{"type":"string"},"params":{"type":"object"},"createdAt":{"type":"string","format":"date-time"},"url":{"type":["string","null"],"description":"Presigned download URL once succeeded."},"thumbUrl":{"type":["string","null"]},"error":{"type":["string","null"],"description":"Customer-safe failure reason."},"priceUsd":{"type":["number","null"],"description":"List price in USD."},"expectedSeconds":{"type":"number","description":"Rough render time."}}},"Upload":{"type":"object","required":["id","kind","filename","url"],"properties":{"id":{"type":"string","description":"asset:<uuid>; use it in references, firstFrame or message attachments."},"kind":{"type":"string"},"filename":{"type":"string"},"url":{"type":"string","description":"Presigned download URL."}}},"Session":{"type":"object","required":["id","title","createdAt","updatedAt","running","chatModel"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"running":{"type":"boolean","description":"A turn is running."},"chatModel":{"type":"string","description":"The model the studio agent runs this session on (GET /chat-models); \"standard\" when none was picked."}}},"ChatModel":{"type":"object","required":["id","label","inputPerMillion","outputPerMillion"],"properties":{"id":{"type":"string","description":"Pass as \"chatModel\"."},"label":{"type":"string"},"inputPerMillion":{"type":["number","null"],"description":"USD per 1M input tokens."},"outputPerMillion":{"type":["number","null"],"description":"USD per 1M output tokens."}}},"ApiMessage":{"type":"object","required":["id","role","text","tools","createdAt"],"properties":{"id":{"type":"string"},"role":{"type":"string","enum":["user","assistant"]},"text":{"type":"string"},"tools":{"type":"array","items":{"type":"object","required":["name","status"],"properties":{"name":{"type":"string"},"status":{"type":"string","enum":["running","done","error"]}}}},"createdAt":{"type":"string","format":"date-time"}}},"TurnResult":{"type":"object","required":["status","reply","messages","generations"],"properties":{"status":{"type":"string","enum":["complete","running","failed","stopped"]},"reply":{"type":"string","description":"The assistant's text for this turn so far."},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ApiMessage"},"description":"This turn's messages."},"generations":{"type":"array","items":{"$ref":"#/components/schemas/PlaygroundGeneration"},"description":"Media started this turn."}}},"Voice":{"type":"object","required":["id","ref","source","label","durationSeconds","audioUrl","consented"],"properties":{"id":{"type":"string"},"ref":{"type":"string","description":"voice:<uuid>, what the studio agent saves on a character."},"source":{"type":"string","enum":["design","upload","research"]},"label":{"type":"string"},"description":{"type":["string","null"]},"durationSeconds":{"type":"number"},"audioUrl":{"type":["string","null"],"description":"Presigned mp3."},"consented":{"type":"boolean"}}},"Event_turn_started":{"type":"object","description":"The turn began.","required":["event","id","data"],"properties":{"event":{"const":"turn.started"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["sessionId","messageId"],"properties":{"sessionId":{"type":"string"},"messageId":{"type":"string"}}}}},"Event_message_delta":{"type":"object","description":"Assistant text as it streams.","required":["event","id","data"],"properties":{"event":{"const":"message.delta"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["messageId","text"],"properties":{"messageId":{"type":"string"},"text":{"type":"string"}}}}},"Event_tool_started":{"type":"object","description":"The agent called a tool.","required":["event","id","data"],"properties":{"event":{"const":"tool.started"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["messageId","toolCallId","name"],"properties":{"messageId":{"type":"string"},"toolCallId":{"type":"string"},"name":{"type":"string"}}}}},"Event_tool_completed":{"type":"object","description":"A tool call finished.","required":["event","id","data"],"properties":{"event":{"const":"tool.completed"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["messageId","toolCallId","name","ok"],"properties":{"messageId":{"type":"string"},"toolCallId":{"type":"string"},"name":{"type":"string"},"ok":{"type":"boolean"},"summary":{"type":"string","description":"Customer-safe."}}}}},"Event_generation_created":{"type":"object","description":"A still, clip or song started by the turn.","required":["event","id","data"],"properties":{"event":{"const":"generation.created"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["generation"],"properties":{"generation":{"$ref":"#/components/schemas/PlaygroundGeneration"}}}}},"Event_generation_updated":{"type":"object","description":"A generation changed status (url set once succeeded).","required":["event","id","data"],"properties":{"event":{"const":"generation.updated"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["generation"],"properties":{"generation":{"$ref":"#/components/schemas/PlaygroundGeneration"}}}}},"Event_turn_completed":{"type":"object","description":"The turn ended. Last event of a message stream.","required":["event","id","data"],"properties":{"event":{"const":"turn.completed"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["messageId","reply"],"properties":{"messageId":{"type":"string"},"reply":{"type":"string","description":"The turn's full assistant text."}}}}},"Event_turn_failed":{"type":"object","description":"The turn failed. Last event of a message stream.","required":["event","id","data"],"properties":{"event":{"const":"turn.failed"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Customer-safe reason."}}}}},"Event_turn_stopped":{"type":"object","description":"The turn was stopped. Last event of a message stream.","required":["event","id","data"],"properties":{"event":{"const":"turn.stopped"},"id":{"type":"string","description":"Monotonic event id; send it back as Last-Event-ID (or ?after=) to resume."},"data":{"type":"object"}}}}}}