{"openapi":"3.1.0","info":{"title":"Lumavox API","version":"0.58.6","description":"Speech, conversation and generative media: text to speech and transcription,\nvoices, images, video, 360° skyboxes, and agents that hold a conversation\nserver-side.\n\n**Everything this reference does not say is in the docs, https://docs.lumavox.ai** —\nauthentication and scopes, queued jobs, audio formats, errors and limits, and\nthe agent's WebSocket, which OpenAPI cannot describe. An agent should start\nat https://docs.lumavox.ai/llms.txt: every page is Markdown at its address plus `.md`.\n\nOperations take a token — `Authorization: Bearer <token>`, or\n`X-API-Key: <token>` — but for the few marked otherwise. Keep API keys on\nyour server; an agent's conversations take an end user's ephemeral token\ninstead.\n"},"tags":[{"name":"tts","description":"Speech generation"},{"name":"images","description":"Image generation"},{"name":"videos","description":"Video generation"},{"name":"skybox","description":"360° environments — an 8K HDRI for lighting a 3D scene, an equirectangular PNG and a depth map. One flat price per generation, whatever you ask for"},{"name":"templates","description":"Generations somebody has already designed — the prompt, the size, every parameter — leaving only the media inputs for you to fill. Priced up front, and polled on the capability's own endpoint"},{"name":"audio","description":"Speech as a queued generation — stored, and the only door that encodes mp3 or opus"},{"name":"transcription","description":"Speech to text — inline for short audio, `stream=true` for newline-delimited segments, queued for long"},{"name":"voices","description":"The voices a key may speak with, and — on `voices:write` — adding, designing, editing and deleting your own"},{"name":"agents","description":"Authoring agents: the characters `/v1/agent/*` talks to, their organisation-wide defaults, and what each may look up. Reads on `agent`, writes on `agent:write`"},{"name":"knowledge","description":"What you tell your agents — entries an agent can look up by meaning, written and assigned from your own systems"},{"name":"tools","description":"What your agents can call while they reply — your server, signed, or the person's app over the WebSocket — each turned on per agent. Reads on `agent`, writes on `agent:write`"},{"name":"agent","description":"Conversations with an agent. There is a streaming door too, on a WebSocket that OpenAPI cannot describe — see *The WebSocket* in the docs"},{"name":"assets","description":"The library — everything your services have produced and you have uploaded. Reads and uploads on `assets`; renaming, moving, deleting and folders on `assets:write`"},{"name":"generations","description":"Every generation across every capability, in one list — the way back to an id you lost, and the reconciliation loop"},{"name":"usage","description":"What your organisation has spent, and what it has left"},{"name":"end users","description":"The people your agents talk to and you generate on behalf of"},{"name":"ephemeral keys","description":"Short-lived tokens for one end user's device, with their own credit ceiling"},{"name":"system","description":"Health and metadata"}],"security":[{"BearerToken":[]},{"ApiKeyHeader":[]}],"components":{"schemas":{"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"version":{"type":"string","description":"The API's version.","examples":["0.35.0"]}},"required":["status","version"]},"ErrorResponse":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/ErrorInfo"}},"required":["error"]},"ErrorInfo":{"type":"object","properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"What went wrong, in words"},"request_id":{"type":"string","description":"Echo of `X-Request-Id` — include it when reporting a problem"},"issues":{"type":"array","items":{"$ref":"#/components/schemas/ValidationIssue"},"description":"Where the request failed validation; empty otherwise"}},"required":["code","message","request_id","issues"]},"ErrorCode":{"type":"string","enum":["invalid_request","unauthenticated","insufficient_credits","forbidden","waitlisted","not_found","method_not_allowed","conflict","too_large","validation_failed","rate_limited","internal","provider_failed","unavailable"],"description":"What went wrong, for a client to switch on: one per HTTP status, except `waitlisted`, a 403 of its own. New codes may be added."},"ValidationIssue":{"type":"object","properties":{"location":{"type":"string","enum":["body","query","path","header"]},"field":{"type":["string","null"],"description":"The dotted path to the field (`voice_config.sample_rate`, `agent_ids.2`); `null` for the whole body."},"message":{"type":"string"}},"required":["location","field","message"]},"PlatformLimitPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlatformLimit"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"PlatformLimit":{"type":"object","properties":{"key":{"type":"string","description":"The limit's name, which this reference's descriptions use for it.","examples":["limits.agent.tools"]},"label":{"type":"string","description":"What it limits, in words.","examples":["Tools on one agent"]},"value":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"}],"description":"The limit in force now, in `unit`.","examples":[20]},"unit":{"type":["string","null"],"enum":["ms","seconds","minutes","hours","days","bytes","credits",null],"description":"What `value` counts; null for a plain count. `credits` are whole credits, not micro-credits."},"default":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"}],"description":"The platform's built-in value, which `value` may differ from.","examples":[20]}},"required":["key","label","value","unit","default"]},"TemplateInput":{"type":"object","properties":{"slot":{"type":"string","description":"What to name it in `inputs` when generating.","examples":["reference_1","start_frame","prompt"]},"kind":{"type":"string","enum":["image","text"],"description":"`image` takes an `ast_…` id from your library; `text` takes what you type — the prompt, when the template leaves it to you."},"label":{"type":"string"},"hint":{"type":["string","null"]},"required":{"type":"boolean"},"example_url":{"type":["string","null"],"description":"For an image input: a picture of the kind of thing that belongs here — the one the example output was generated from."},"example":{"type":["string","null"],"description":"For a text input: an example of what to write — the text the example output was generated from."}},"required":["slot","kind","label","hint","required","example_url","example"]},"SkyboxStylePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SkyboxStyle"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"SkyboxStyle":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"max_prompt_chars":{"type":["integer","null"]},"max_negative_chars":{"type":["integer","null"]},"model_version":{"type":["string","null"],"description":"Which of the provider's model generations it runs on."},"image":{"type":["string","null"],"description":"A sample image of the style, if they gave one."}},"required":["id","name","max_prompt_chars","max_negative_chars","model_version","image"]},"EphemeralKeyLimits":{"type":"object","properties":{"max_ttl_seconds":{"type":"integer","description":"The platform's longest lifetime for a key."},"default_ttl_seconds":{"type":"integer"},"max_credit_limit_micros":{"type":"integer","description":"The platform's largest credit limit for a key.","examples":[2500000]},"plan_max_ttl_seconds":{"type":["integer","null"],"description":"Your plan's longest lifetime for one end user's key; `null` sets none. A key is held to the tighter of this and `max_ttl_seconds`."},"plan_max_credit_limit_micros":{"type":["integer","null"],"description":"Your plan's largest credit limit for one end user's key, in micro-credits; `null` sets none. A key is held to the tighter of this and `max_credit_limit_micros`."},"plan_max_live_keys":{"type":["integer","null"],"description":"How many live keys one end user may hold on your plan; `null` sets none."},"grantable_scopes":{"type":"array","items":{"type":"string"}}},"required":["max_ttl_seconds","default_ttl_seconds","max_credit_limit_micros","plan_max_ttl_seconds","plan_max_credit_limit_micros","plan_max_live_keys","grantable_scopes"]},"EphemeralKeyCreateResponse":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"prefix":{"type":"string"},"last4":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"credit_limit_micros":{"type":"integer","description":"The most this key may spend, in micro-credits.","examples":[2500000]},"credits_spent_micros":{"type":"integer","description":"What it has spent, as its end user DO last settled — at the end, or daily for a long-lived key. Final once `settled_at` is set.","examples":[2500000]},"credits_remaining_micros":{"type":"integer","description":"Micro-credits: 1 credit is 1,000,000.","examples":[2500000]},"agent_ids":{"type":["array","null"],"items":{"type":"string"}},"created_by_api_key_id":{"type":["string","null"]},"created_by_clerk_user_id":{"type":["string","null"]},"end_user_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"expires_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"last_used_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"revoked_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"revoked_reason":{"type":["string","null"]},"settled_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When its end user DO settled it for the last time — what it spent and what came back are final from then. `null` while it is live, and for a few seconds after it ends, longer while a memory job it paid for still runs."},"active":{"type":"boolean"},"key":{"type":"string","description":"The token, in full. **Shown once**: it is signed rather than stored, so it cannot be shown again."}},"required":["id","label","prefix","last4","scopes","credit_limit_micros","credits_spent_micros","credits_remaining_micros","agent_ids","created_by_api_key_id","created_by_clerk_user_id","end_user_id","created_at","expires_at","last_used_at","revoked_at","revoked_reason","settled_at","active","key"]},"EphemeralKeyCreateRequest":{"type":"object","properties":{"scopes":{"type":"array","items":{"type":"string"},"default":["agent"]},"credit_limit_micros":{"type":"integer","exclusiveMinimum":0,"description":"The most this key may spend, in micro-credits (1 credit is 1,000,000). Required — there is no unlimited key."},"expires_in_seconds":{"type":"integer","exclusiveMinimum":0,"default":900},"end_user_id":{"type":"string"},"agent_ids":{"type":["array","null"],"items":{"type":"string"},"default":null},"label":{"type":["string","null"],"maxLength":120,"default":null}},"required":["credit_limit_micros","end_user_id"],"additionalProperties":false},"EphemeralKeyPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EphemeralKeyInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"EphemeralKeyInfo":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"prefix":{"type":"string"},"last4":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"credit_limit_micros":{"type":"integer","description":"The most this key may spend, in micro-credits.","examples":[2500000]},"credits_spent_micros":{"type":"integer","description":"What it has spent, as its end user DO last settled — at the end, or daily for a long-lived key. Final once `settled_at` is set.","examples":[2500000]},"credits_remaining_micros":{"type":"integer","description":"Micro-credits: 1 credit is 1,000,000.","examples":[2500000]},"agent_ids":{"type":["array","null"],"items":{"type":"string"}},"created_by_api_key_id":{"type":["string","null"]},"created_by_clerk_user_id":{"type":["string","null"]},"end_user_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"expires_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"last_used_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"revoked_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"revoked_reason":{"type":["string","null"]},"settled_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When its end user DO settled it for the last time — what it spent and what came back are final from then. `null` while it is live, and for a few seconds after it ends, longer while a memory job it paid for still runs."},"active":{"type":"boolean"}},"required":["id","label","prefix","last4","scopes","credit_limit_micros","credits_spent_micros","credits_remaining_micros","agent_ids","created_by_api_key_id","created_by_clerk_user_id","end_user_id","created_at","expires_at","last_used_at","revoked_at","revoked_reason","settled_at","active"]},"TokenSelf":{"oneOf":[{"$ref":"#/components/schemas/EphemeralTokenSelf"},{"$ref":"#/components/schemas/ApiKeySelf"}],"discriminator":{"propertyName":"kind","mapping":{"ephemeral":"#/components/schemas/EphemeralTokenSelf","api_key":"#/components/schemas/ApiKeySelf"}}},"EphemeralTokenSelf":{"type":"object","properties":{"kind":{"type":"string","enum":["ephemeral"]},"scopes":{"type":"array","items":{"type":"string"}},"credit_limit_micros":{"type":"integer","description":"Micro-credits: 1 credit is 1,000,000.","examples":[2500000]},"credits_spent_micros":{"type":"integer","description":"What the key has spent so far, turn by turn.","examples":[2500000]},"credits_remaining_micros":{"type":"integer","description":"Micro-credits: 1 credit is 1,000,000.","examples":[2500000]},"expires_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"expires_in_seconds":{"type":"integer"},"agent_ids":{"type":["array","null"],"items":{"type":"string"}},"end_user_id":{"type":["string","null"]}},"required":["kind","scopes","credit_limit_micros","credits_spent_micros","credits_remaining_micros","expires_at","expires_in_seconds","agent_ids","end_user_id"]},"ApiKeySelf":{"type":"object","properties":{"kind":{"type":"string","enum":["api_key"]},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"is_master":{"type":"boolean"},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]}},"required":["kind","name","scopes","is_master","created_at"]},"UsageResponse":{"type":"object","properties":{"org_id":{"type":"string"},"since":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"until":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"plan":{"anyOf":[{"$ref":"#/components/schemas/OrgPlan"},{"type":"null"}],"description":"The organisation's subscription and remaining credits, or null if it is not on a plan."},"total_requests":{"type":"integer"},"total_seconds":{"type":"number"},"by_capability":{"type":"array","items":{"$ref":"#/components/schemas/UsageSummary"}},"daily":{"type":"array","items":{"$ref":"#/components/schemas/UsageDay"},"description":"One entry per UTC day across the whole window, **including days with no traffic**. Gap-filled server-side so a chart can plot it directly rather than every client reinventing the same fill."},"by_key":{"type":"array","items":{"$ref":"#/components/schemas/UsageByKey"},"description":"Which credential spent the quota, busiest first."}},"required":["org_id","since","until","plan","total_requests","total_seconds","by_capability","daily","by_key"]},"OrgPlan":{"type":"object","properties":{"plan_slug":{"type":"string"},"plan_name":{"type":"string"},"monthly_credits_micros":{"type":"integer","description":"What the plan grants each cycle.","examples":[2500000]},"credit_balance_micros":{"type":"integer","description":"What is left: may exceed the grant after a manual adjustment, and be slightly negative inside the grace buffer.","examples":[2500000]},"period_end":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]}},"required":["plan_slug","plan_name","monthly_credits_micros","credit_balance_micros","period_end"]},"UsageSummary":{"type":"object","properties":{"capability":{"type":"string"},"model":{"type":["string","null"]},"event_type":{"type":["string","null"]},"requests":{"type":"integer"},"units_seconds":{"type":"number"},"credits_micros":{"type":["integer","null"],"description":"What was charged; null when nothing in the group was priced.","examples":[2500000]},"unpriced_requests":{"type":"integer","description":"Requests in this group with no price recorded. Their credits are not in the sum, so a non-zero value here means the total is a floor."}},"required":["capability","model","event_type","requests","units_seconds","credits_micros","unpriced_requests"]},"UsageDay":{"type":"object","properties":{"date":{"type":"string","format":"date"},"requests":{"type":"integer"},"units_seconds":{"type":"number"}},"required":["date","requests","units_seconds"]},"UsageByKey":{"type":"object","properties":{"api_key_id":{"type":["string","null"],"description":"Null once the key has been deleted"},"name":{"type":"string","description":"Key label, or 'Deleted key' if it is gone"},"requests":{"type":"integer"},"units_seconds":{"type":"number"},"credits_micros":{"type":["integer","null"],"description":"What was charged; null when nothing in the group was priced.","examples":[2500000]},"unpriced_requests":{"type":"integer","description":"Requests in this group with no price recorded. Their credits are not in the sum, so a non-zero value here means the total is a floor."}},"required":["api_key_id","name","requests","units_seconds","credits_micros","unpriced_requests"]},"GenerationInfo":{"type":"object","properties":{"id":{"type":"string","description":"`gen_…`. Poll this.","examples":["gen_a1B2c3D4e5F6g7H8"]},"status":{"type":"string","enum":["queued","submitted","running","succeeded","failed","moderated"],"description":"`queued`, `submitted` and `running` mean keep polling. `succeeded`, `failed` and `moderated` are terminal — stop."},"capability":{"type":"string","enum":["tts","image","video","stt","skybox"]},"model":{"type":["string","null"]},"prompt":{"type":["string","null"]},"aspect_ratio":{"type":["string","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"output_format":{"type":["string","null"]},"seed":{"type":["integer","null"]},"megapixels":{"type":["number","null"]},"safety_tolerance":{"type":["integer","null"]},"enhance_prompt":{"type":["boolean","null"]},"folder_id":{"type":["string","null"],"description":"`fld_…` the result lands in, or null for the root."},"duration":{"type":["integer","null"],"description":"Seconds asked for. Video only."},"resolution":{"type":["string","null"],"description":"`hd` or `fhd`. Video only."},"generate_audio":{"type":["boolean","null"]},"start_frame":{"type":["string","null"]},"end_frame":{"type":["string","null"]},"negative_text":{"type":["string","null"]},"style_id":{"type":["integer","null"]},"init_image":{"type":["string","null"]},"init_strength":{"type":["number","null"]},"voice_id":{"type":["string","null"]},"speech":{"anyOf":[{"$ref":"#/components/schemas/TTSOptions"},{"type":"null"}],"description":"The voice and model a speech generation ran with."},"credits_micros":{"type":["integer","null"],"description":"What was debited when this was accepted, in micro-credits (1 credit is 1,000,000). A failure refunds it in full; a transcription's is corrected to the measured length when it finishes.","examples":[2500000]},"template_id":{"type":["string","null"]},"image":{"anyOf":[{"$ref":"#/components/schemas/GenerationImage"},{"type":"null"}]},"video":{"anyOf":[{"$ref":"#/components/schemas/GenerationVideo"},{"type":"null"}]},"audio":{"anyOf":[{"$ref":"#/components/schemas/GenerationAudio"},{"type":"null"}]},"transcript":{"anyOf":[{"$ref":"#/components/schemas/GenerationTranscript"},{"type":"null"}]},"skybox":{"anyOf":[{"$ref":"#/components/schemas/GenerationSkybox"},{"type":"null"}]},"error":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"finished_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"]}},"required":["id","status","capability","model","prompt","aspect_ratio","width","height","output_format","seed","megapixels","safety_tolerance","enhance_prompt","folder_id","duration","resolution","generate_audio","start_frame","end_frame","negative_text","style_id","init_image","init_strength","voice_id","speech","credits_micros","template_id","image","video","audio","transcript","skybox","error","created_at","finished_at"]},"TTSOptions":{"type":"object","properties":{"voice_id":{"type":"string","description":"An id from `GET /v1/voices` — the built-in voice, a platform voice, or one your organisation uploaded."},"model":{"type":"string","description":"Which model speaks: one of the voice's `models` in `GET /v1/voices` — `eleven_flash_v2_5` (the fastest, and the default), `eleven_multilingual_v2` or `eleven_v3`. A model the voice does not list is refused."},"provider_options":{"anyOf":[{"$ref":"#/components/schemas/ProviderVoiceSettingsInfo"},{"type":"null"}],"description":"The provider's voice settings sent on top of the voice's own; `null` took theirs."},"format":{"type":"string","enum":["wav","pcm"]},"sample_rate":{"type":["integer","null"],"enum":[8000,16000,22050,24000,44100,null],"description":"Hz of the returned audio — one of 8000, 16000, 22050, 24000, 44100, each served natively by the provider. Unset means 24000.\n\n`POST /v1/tts` says what it produced in the `X-Sample-Rate` header. Queued speech (`POST /v1/audio`) does not take it: each of its formats has its own rate."}},"required":["voice_id","model","provider_options","format","sample_rate"]},"ProviderVoiceSettingsInfo":{"type":"object","properties":{"stability":{"type":["number","null"],"description":"Low is more expressive and less consistent between generations; high is flatter and more repeatable."},"similarity_boost":{"type":["number","null"],"description":"How closely to cling to the original voice. High can reproduce artefacts in the source."},"style":{"type":["number","null"],"description":"Style exaggeration. Costs latency; leave unset on anything conversational."},"use_speaker_boost":{"type":["boolean","null"],"description":"Similarity at the cost of latency."},"speed":{"type":["number","null"],"description":"1.0 is the voice's own pace."}},"required":["stability","similarity_boost","style","use_speaker_boost","speed"]},"GenerationImage":{"type":"object","properties":{"asset_id":{"type":["string","null"],"description":"The library entry this generation produced."},"url":{"type":["string","null"],"description":"A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one."},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"content_type":{"type":["string","null"]},"size_bytes":{"type":["integer","null"]}},"required":["asset_id","url","width","height","content_type","size_bytes"]},"GenerationVideo":{"type":"object","properties":{"asset_id":{"type":["string","null"],"description":"The library entry this generation produced."},"url":{"type":["string","null"],"description":"A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one."},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"duration_seconds":{"type":["number","null"]},"content_type":{"type":["string","null"]},"size_bytes":{"type":["integer","null"]},"has_thumbnail":{"type":["boolean","null"]}},"required":["asset_id","url","width","height","duration_seconds","content_type","size_bytes","has_thumbnail"]},"GenerationAudio":{"type":"object","properties":{"asset_id":{"type":["string","null"],"description":"The library entry this generation produced."},"url":{"type":["string","null"],"description":"A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one."},"duration_seconds":{"type":["number","null"]},"content_type":{"type":["string","null"]},"size_bytes":{"type":["integer","null"]},"peaks":{"type":["array","null"],"items":{"type":"integer"},"description":"Waveform magnitudes, 0-100, for a mirrored strip; null when never computed."}},"required":["asset_id","url","duration_seconds","content_type","size_bytes","peaks"]},"GenerationTranscript":{"type":"object","properties":{"text":{"type":["string","null"],"description":"The transcript, as one string."},"language_code":{"type":["string","null"]},"language_probability":{"type":["number","null"]},"duration_seconds":{"type":["number","null"]},"word_count":{"type":["integer","null"]},"asset_id":{"type":["string","null"],"description":"The library entry holding the full JSON."},"content_type":{"type":["string","null"]},"size_bytes":{"type":["integer","null"]},"url":{"type":["string","null"]}},"required":["text","language_code","language_probability","duration_seconds","word_count","asset_id","content_type","size_bytes","url"]},"GenerationSkybox":{"type":"object","properties":{"asset_id":{"type":["string","null"],"description":"The library entry this generation produced."},"url":{"type":["string","null"],"description":"A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one."},"preview_url":{"type":["string","null"],"description":"A short-lived link to the 2K WebP, which is what to load in a 360 viewer."},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"content_type":{"type":["string","null"]},"size_bytes":{"type":["integer","null"]},"has_thumbnail":{"type":["boolean","null"]},"files":{"type":"array","items":{"$ref":"#/components/schemas/SkyboxFile"},"description":"Every file the environment was stored as, in plan order."}},"required":["asset_id","url","preview_url","width","height","content_type","size_bytes","has_thumbnail","files"]},"SkyboxFile":{"type":"object","properties":{"name":{"type":"string","description":"`hdr-4k`, `hdr-2k`, `png-8k`, `png-4k`, `png-2k`, `preview-2k`, `depth-2k`.","examples":["hdr-4k"]},"content_type":{"type":"string"},"url":{"type":["string","null"],"description":"Short-lived link. Minted per response and not stored."},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"size_bytes":{"type":["integer","null"]}},"required":["name","content_type","url","width","height","size_bytes"]},"ImageRequest":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":4000,"description":"What to generate.","examples":["A weathered brass diving helmet on wet slate, low winter light"]},"aspect_ratio":{"type":"string","default":"1:1","description":"One of: 21:9, 16:9, 4:3, 1:1, 3:4, 9:16."},"megapixels":{"type":"number","minimum":0.25,"maximum":4,"default":1,"description":"Output size. **This and your references are what you are charged on** — the price is per megapixel, the output's with a one-megapixel minimum and each reference's at the same rate on its own size, so a request's cost is known before it is made."},"output_format":{"type":"string","default":"jpeg","description":"One of: jpeg, png, webp."},"seed":{"type":["integer","null"],"minimum":0,"description":"Reuse a seed to get the same image again from the same prompt. Omit for a different one each time."},"safety_tolerance":{"type":"integer","minimum":0,"maximum":5,"default":2,"description":"0 is strictest. Content refused at any setting is refused."},"enhance_prompt":{"type":"boolean","default":true,"description":"Let the model rewrite your prompt before rendering. On by default, which is the provider's own behaviour. Turn it off to send your words exactly as written."},"references":{"type":"array","items":{"$ref":"#/components/schemas/Reference"},"maxItems":8,"default":[],"description":"Up to eight images from your library for the model to work from. **Input and output share a 9 megapixel budget** — at a 1 MP output that is eight references, at 2 MP it is seven — so send a smaller `variant` when you need more of them. Over budget is a 400 naming the numbers, never a silent drop. **Each reference is charged** at the output's per-megapixel rate, on the megapixels of the copy sent, with no minimum."},"folder_id":{"type":["string","null"],"description":"`fld_…` — the folder in your asset library the finished file is filed under. Omit it for the root. A folder that is not yours is a 400, never a silent fall back to the root.","examples":["fld_a1B2c3D4e5F6g7H8"]}},"required":["prompt"],"additionalProperties":false},"Reference":{"type":"object","properties":{"asset_id":{"type":"string","description":"`ast_…` — an image in your library.","examples":["ast_a1B2c3D4e5F6g7H8"]},"variant":{"type":"string","default":"original","description":"Which copy to send: `original`, or one of `50`, `25`, `12.5` — the smaller versions made when the image was stored. Smaller copies spend less of the 9 megapixel budget shared with the output, and cost less: a reference is charged on the copy sent. A variant that does not exist falls back to the original, which may put the request over budget."}},"required":["asset_id"],"additionalProperties":false},"VideoRequest":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":4000,"description":"What should happen in the clip."},"aspect_ratio":{"type":"string","default":"16:9","description":"One of: 21:9, 2:1, 16:9, 4:3, 1:1, 3:4, 9:16. Note this is **not** the same set images accept — video adds `2:1`."},"duration":{"type":"integer","minimum":5,"maximum":20,"default":5,"description":"Whole seconds. **This is what you are charged on** — the price is per second at a rate that depends on `resolution`."},"resolution":{"type":"string","default":"hd","description":"One of: hd, fhd. `hd` is 720p and `fhd` is 1080p, and they are **priced differently per second**."},"generate_audio":{"type":"boolean","default":true},"safety_tolerance":{"type":"integer","minimum":0,"maximum":4,"default":2,"description":"0 is strictest. **The ceiling is 4 here, not the 5 images allow.**"},"start_frame":{"type":["string","null"],"description":"`ast_…` — an image in your library to open the clip on. Omit for text-to-video."},"end_frame":{"type":["string","null"],"description":"`ast_…` — an image to close the clip on, pinned at `duration`. **Requires `start_frame`**."},"folder_id":{"type":["string","null"],"description":"`fld_…` — the folder in your asset library the finished file is filed under. Omit it for the root. A folder that is not yours is a 400, never a silent fall back to the root.","examples":["fld_a1B2c3D4e5F6g7H8"]}},"required":["prompt"],"additionalProperties":false},"AudioRequest":{"type":"object","properties":{"voice_id":{"type":"string","default":"default","description":"An id from `GET /v1/voices` — the built-in voice, a platform voice, or one your organisation uploaded."},"model":{"type":"string","default":"eleven_flash_v2_5","description":"Which model speaks: one of the voice's `models` in `GET /v1/voices` — `eleven_flash_v2_5` (the fastest, and the default), `eleven_multilingual_v2` or `eleven_v3`. A model the voice does not list is refused."},"provider_options":{"anyOf":[{"$ref":"#/components/schemas/ProviderVoiceSettings"},{"type":"null"}],"default":null,"description":"The provider's voice settings, on top of the ones the voice was registered with. Unset takes theirs."},"format":{"type":"string","enum":["wav","mp3","opus"],"default":"mp3","description":"One of: wav, mp3, opus."},"sample_rate":{"type":["integer","null"],"enum":[8000,16000,22050,24000,44100,null],"description":"Hz of the returned audio — one of 8000, 16000, 22050, 24000, 44100, each served natively by the provider. Unset means 24000.\n\n`POST /v1/tts` says what it produced in the `X-Sample-Rate` header. Queued speech (`POST /v1/audio`) does not take it: each of its formats has its own rate."},"text":{"type":"string","minLength":1,"maxLength":5000,"description":"What to say.","examples":["Oh wow, I was not expecting that at all!"]},"folder_id":{"type":["string","null"],"description":"`fld_…` — the folder in your asset library the finished file is filed under. Omit it for the root. A folder that is not yours is a 400, never a silent fall back to the root.","examples":["fld_a1B2c3D4e5F6g7H8"]}},"required":["text"],"additionalProperties":false},"ProviderVoiceSettings":{"type":"object","properties":{"stability":{"type":["number","null"],"minimum":0,"maximum":1,"default":null,"description":"Low is more expressive and less consistent between generations; high is flatter and more repeatable."},"similarity_boost":{"type":["number","null"],"minimum":0,"maximum":1,"default":null,"description":"How closely to cling to the original voice. High can reproduce artefacts in the source."},"style":{"type":["number","null"],"minimum":0,"maximum":1,"default":null,"description":"Style exaggeration. Costs latency; leave unset on anything conversational."},"use_speaker_boost":{"type":["boolean","null"],"default":null,"description":"Similarity at the cost of latency."},"speed":{"type":["number","null"],"minimum":0.7,"maximum":1.2,"default":null,"description":"1.0 is the voice's own pace."}},"additionalProperties":false},"SkyboxRequest":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":550,"description":"The environment to build. Describe the whole surrounding — sky, ground, horizon and light — rather than a subject.","examples":["A salt flat at blue hour, thin cloud, distant mesas"]},"negative_text":{"type":["string","null"],"maxLength":200,"description":"What to keep out of it."},"style_id":{"type":["integer","null"],"minimum":1,"description":"One of the provider's styles — fetch the current list from `GET /v1/skybox/styles`. Omit for their default."},"seed":{"type":["integer","null"],"minimum":1,"maximum":2147483647,"description":"Reuse a seed to get the same environment again. **Note this starts at 1, not 0** — the provider uses zero for random."},"enhance_prompt":{"type":"boolean","default":false},"init_image":{"type":["string","null"],"description":"`ast_…` — an existing **equirectangular** image in your library to build from.","examples":["ast_a1B2c3D4e5F6g7H8"]},"init_strength":{"type":"number","minimum":0,"maximum":0.97,"default":0.5,"description":"How much the init image influences the result: 0 ignores it, 0.97 is the provider's maximum. Only has an effect alongside `init_image`."},"folder_id":{"type":["string","null"],"description":"`fld_…` — the folder in your asset library the finished file is filed under. Omit it for the root. A folder that is not yours is a 400, never a silent fall back to the root.","examples":["fld_a1B2c3D4e5F6g7H8"]}},"required":["prompt"],"additionalProperties":false},"GenerationPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/GenerationInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"TranscribeAssetRequest":{"type":"object","properties":{"asset_id":{"type":"string","minLength":3,"maxLength":32,"description":"`ast_…`, from your asset library. Must be audio.","examples":["ast_a1B2c3D4e5F6g7H8"]},"model":{"type":"string","enum":["scribe_v2"],"default":"scribe_v2","description":"One of: scribe_v2."},"language":{"type":["string","null"],"default":null,"description":"ISO-639-1 or -639-3 code. Omit it and the language is detected automatically. Set it when you already know -- detection is very good and not free of mistakes, and a wrong guess costs the whole transcript.\n\n**`language_code` on the response is not this value.** It is ISO-639-3 whichever you sent, so `en` here comes back as `eng`.","examples":["en","fa","de"]},"diarize":{"type":"boolean","default":false,"description":"Label who is speaking. Each word gains a `speaker_id`. Adds processing time and is not free of error on overlapping speech."},"num_speakers":{"type":["integer","null"],"minimum":1,"maximum":32,"default":null,"description":"The most speakers to look for, when `diarize` is on. Ignored otherwise. Setting it when you know the number improves the labelling; leaving it out lets the provider decide."},"tag_audio_events":{"type":"boolean","default":false,"description":"Mark laughter, applause and similar in the transcript, as `(laughter)`-style entries with `type: audio_event`."},"timestamps":{"type":"boolean","default":false,"description":"Return each word with its `start_seconds` and `end_seconds`. Off by default because the array is far larger than the text -- an hour of speech is roughly ten thousand entries -- and most callers want the text."},"folder_id":{"type":["string","null"],"description":"`fld_…` — the folder in your asset library the finished file is filed under. Omit it for the root. A folder that is not yours is a 400, never a silent fall back to the root.","examples":["fld_a1B2c3D4e5F6g7H8"]}},"required":["asset_id"],"additionalProperties":false},"TemplatePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TemplateInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"TemplateInfo":{"type":"object","properties":{"id":{"type":"string","description":"`tpl_…`.","examples":["tpl_a1B2c3D4e5F6g7H8"]},"name":{"type":"string"},"description":{"type":["string","null"]},"capability":{"type":"string","enum":["image","video","skybox"],"description":"What it generates, and so which scope the key needs to generate from it and which endpoint the result is polled on."},"inputs":{"type":"array","items":{"$ref":"#/components/schemas/TemplateInput"},"description":"What to supply, in the order they are drawn."},"example_url":{"type":["string","null"],"description":"What it produces: a real output, generated by the platform from the example inputs shown beside each slot."},"credits_micros":{"type":["integer","null"],"description":"What one generation from it costs your organisation, in micro-credits (1 credit is 1,000,000). Knowable up front because every parameter is frozen — but on an image template your own pictures are charged on top, each at the output's per-megapixel rate and the size sent. Null when the platform has no rate for the capability.","examples":[2500000]},"fixed_megapixels":{"type":"number","description":"On an image template, the megapixels of the pictures the template itself sends with every generation, each charged at the output's per-megapixel rate and already in `credits_micros` — so an estimate that moves the output's size keeps this term as it is, and adds your own pictures at the size they are sent. 0 on a video or skybox template, whose price no picture changes.","examples":[2]},"parameters":{"type":"object","additionalProperties":{},"description":"The request the template froze, minus the prompt. Every entry is decided for you unless its name is in `exposed`, in which case it is the default you may override."},"exposed":{"type":"array","items":{"type":"string"},"description":"The parameters you may change, by name — pass a new value in `parameters` when generating. A change to a size, a duration or a resolution changes the price from the one quoted here.","examples":[["aspect_ratio","megapixels"]]},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]}},"required":["id","name","description","capability","inputs","example_url","credits_micros","fixed_megapixels","parameters","exposed","created_at","updated_at"]},"TemplateGenerateRequest":{"type":"object","properties":{"inputs":{"type":"object","additionalProperties":{"type":"string"},"default":{},"description":"Slot name to value, one entry per input you are filling: an `ast_…` asset id for an `image` input, your text for a `text` one. Every `required` input must be present; an input the template does not open is a 422.","examples":[{"reference_1":"ast_a1B2c3D4e5F6g7H8"},{"prompt":"A red bicycle leaning on a brick wall"}]},"parameters":{"type":"object","additionalProperties":{},"default":{},"description":"Overrides for the parameters the template lists in `exposed`, by name, as `POST /v1/images` (or the capability's own route) would take them. A parameter the template did not expose is a 422 before anything is charged; a value the capability refuses is the same 422 that route would give.","examples":[{"megapixels":2,"aspect_ratio":"16:9"}]},"folder_id":{"type":["string","null"],"description":"`fld_…` — the folder in your asset library the finished file is filed under. Omit it for the root. A folder that is not yours is a 400, never a silent fall back to the root.","examples":["fld_a1B2c3D4e5F6g7H8"]}},"additionalProperties":false},"AssetPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssetSummary"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"AssetSummary":{"type":"object","properties":{"id":{"type":"string","description":"`ast_…`"},"name":{"type":"string"},"kind":{"type":"string","enum":["input","output"],"description":"`input` if you uploaded it, `output` if a service made it"},"media_type":{"type":"string","enum":["audio","image","video","data"],"description":"What to do with the bytes — coarser than `content_type`, and what the library filters on. `data` is a lip-sync control track"},"content_type":{"type":"string","description":"The MIME type","examples":["image/jpeg"]},"size_bytes":{"type":"integer","description":"Size of the stored object"},"duration_seconds":{"type":["number","null"],"description":"How long the audio or video runs; null for a still"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"peaks":{"type":["array","null"],"items":{"type":"integer"},"description":"Waveform magnitudes, 0-100, for a mirrored strip. Empty means silent or unreadable; null means never computed."},"folder_id":{"type":["string","null"],"description":"`fld_…`, or null at the library root"},"capability":{"type":["string","null"],"enum":["tts","image","video","stt","skybox",null],"description":"Which capability produced it. Null for an upload","examples":["image"]},"origin":{"type":["string","null"],"enum":["api","console","end_user","platform",null],"description":"Which door it came through. Null for older rows"},"thumbnail_url":{"type":["string","null"],"description":"A short-lived link to a 512px square preview, for visual media only. Null for audio, for anything not yet converted, and for anything stored before previews existed."},"status":{"type":"string","enum":["pending","ready","failed"],"description":"`pending` while an upload converts, then `ready` or `failed`. Everything a service produces is `ready` immediately."},"error":{"type":["string","null"],"description":"Why an upload could not be converted. Null otherwise."},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]}},"required":["id","name","kind","media_type","content_type","size_bytes","duration_seconds","width","height","peaks","folder_id","capability","origin","thumbnail_url","status","error","created_at"]},"UploadTicket":{"type":"object","properties":{"asset_id":{"type":"string","description":"Pass this back to confirm the upload.","examples":["ast_a1B2c3D4e5F6g7H8"]},"url":{"type":"string","description":"PUT the file here, unmodified, with the `Content-Type` you asked for and no authentication headers — the URL carries its own. It expires; ask for another if it does."},"expires_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When `url` stops accepting the upload."}},"required":["asset_id","url","expires_at"]},"UploadTicketRequest":{"type":"object","properties":{"content_type":{"type":"string","description":"What you are about to upload. Signed into the URL, so the upload must send exactly this `Content-Type`, and checked against the stored object when you confirm.","examples":["image/png"]}},"required":["content_type"],"additionalProperties":false},"AssetDetail":{"type":"object","properties":{"id":{"type":"string","description":"`ast_…`"},"name":{"type":"string"},"kind":{"type":"string","enum":["input","output"],"description":"`input` if you uploaded it, `output` if a service made it"},"media_type":{"type":"string","enum":["audio","image","video","data"],"description":"What to do with the bytes — coarser than `content_type`, and what the library filters on. `data` is a lip-sync control track"},"content_type":{"type":"string","description":"The MIME type","examples":["image/jpeg"]},"size_bytes":{"type":"integer","description":"Size of the stored object"},"duration_seconds":{"type":["number","null"],"description":"How long the audio or video runs; null for a still"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"peaks":{"type":["array","null"],"items":{"type":"integer"},"description":"Waveform magnitudes, 0-100, for a mirrored strip. Empty means silent or unreadable; null means never computed."},"folder_id":{"type":["string","null"],"description":"`fld_…`, or null at the library root"},"capability":{"type":["string","null"],"enum":["tts","image","video","stt","skybox",null],"description":"Which capability produced it. Null for an upload","examples":["image"]},"origin":{"type":["string","null"],"enum":["api","console","end_user","platform",null],"description":"Which door it came through. Null for older rows"},"thumbnail_url":{"type":["string","null"],"description":"A short-lived link to a 512px square preview, for visual media only. Null for audio, for anything not yet converted, and for anything stored before previews existed."},"status":{"type":"string","enum":["pending","ready","failed"],"description":"`pending` while an upload converts, then `ready` or `failed`. Everything a service produces is `ready` immediately."},"error":{"type":["string","null"],"description":"Why an upload could not be converted. Null otherwise."},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"request_id":{"type":["string","null"],"description":"`req_…` — the call that produced this"},"end_user_id":{"type":["string","null"]},"meta":{"type":["object","null"],"additionalProperties":{}},"url":{"type":["string","null"],"description":"Time-limited link to the bytes. Null when object storage is unavailable."},"url_expires_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When `url`, the variants' links and `preview_url` stop working; null when there is no `url`. Fetch the asset again for fresh ones."},"preview_url":{"type":["string","null"],"description":"A displayable copy, when `url` is not displayable. Set for skyboxes, whose `url` is a Radiance `.hdr`; null otherwise."},"variants":{"type":"array","items":{"$ref":"#/components/schemas/VariantInfo"},"description":"Smaller copies of the same image, largest first. Empty for audio and for anything that has none."}},"required":["id","name","kind","media_type","content_type","size_bytes","duration_seconds","width","height","peaks","folder_id","capability","origin","thumbnail_url","status","error","created_at","request_id","end_user_id","meta","url","url_expires_at","preview_url","variants"]},"VariantInfo":{"type":"object","properties":{"name":{"type":"string","description":"`50`, `25` or `12.5` — the percentage of the original"},"width":{"type":"integer"},"height":{"type":"integer"},"size_bytes":{"type":"integer"},"url":{"type":["string","null"],"description":"Time-limited link to this copy, good until the asset's `url_expires_at`."}},"required":["name","width","height","size_bytes","url"]},"UploadConfirm":{"type":"object","properties":{"asset_id":{"type":"string","description":"From the ticket.","examples":["ast_a1B2c3D4e5F6g7H8"]},"content_type":{"type":"string","description":"What was uploaded.","examples":["image/png"]},"filename":{"type":"string","description":"The file's own name; the asset is called this, less its extension, when `name` is not given."},"name":{"type":"string","description":"What to call the asset."},"folder_id":{"type":["string","null"],"description":"`fld_…` — the folder to file it in; omit it, or `null`, for the root. A folder that is not yours is a 404."}},"required":["asset_id","content_type"],"additionalProperties":false},"AssetsAffected":{"type":"object","properties":{"affected":{"type":"integer"}},"required":["affected"]},"AssetBulkRequest":{"type":"object","properties":{"asset_ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":500}},"required":["asset_ids"],"additionalProperties":false},"AssetMoveRequest":{"type":"object","properties":{"asset_ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":500},"folder_id":{"type":["string","null"],"description":"`fld_…`, or null for the root"}},"required":["asset_ids","folder_id"],"additionalProperties":false},"AssetUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"folder_id":{"type":["string","null"],"description":"`fld_…`, or null for the root; omit it to leave it"}},"additionalProperties":false},"FolderPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FolderSummary"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"FolderSummary":{"type":"object","properties":{"id":{"type":"string","description":"`fld_…`"},"name":{"type":"string"},"parent_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"asset_count":{"type":["integer","null"],"description":"Live assets directly in this folder"},"folder_count":{"type":["integer","null"],"description":"Subfolders directly in this folder"}},"required":["id","name","parent_id","created_at","asset_count","folder_count"]},"FolderDetail":{"type":"object","properties":{"id":{"type":"string","description":"`fld_…`"},"name":{"type":"string"},"parent_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"asset_count":{"type":["integer","null"],"description":"Live assets directly in this folder"},"folder_count":{"type":["integer","null"],"description":"Subfolders directly in this folder"},"ancestors":{"type":"array","items":{"$ref":"#/components/schemas/FolderSummary"},"description":"The folders above it, top level first"}},"required":["id","name","parent_id","created_at","asset_count","folder_count","ancestors"]},"FolderCreate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"parent_id":{"type":["string","null"],"description":"`fld_…`; omit it, or `null`, for the top level"}},"required":["name"],"additionalProperties":false},"FolderUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"parent_id":{"type":["string","null"],"description":"`fld_…`, or null for the top level; omit it to leave it"}},"additionalProperties":false},"FolderDeleted":{"type":"object","properties":{"moved_assets":{"type":"integer","description":"Assets that moved up to the parent rather than being deleted"},"moved_folders":{"type":"integer","description":"Subfolders that moved up to the parent"}},"required":["moved_assets","moved_folders"]},"SessionDetail":{"type":"object","properties":{"agent_id":{"type":"string"},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"end_user_id":{"type":"string","description":"Who this conversation is with. Send it again to open another conversation with the same person, carrying their memory.","examples":["eu_7Kq2mZp1XvA9dRt4"]},"id":{"type":"string"},"title":{"type":["string","null"]},"turn_seq":{"type":"integer"},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"messages":{"$ref":"#/components/schemas/MessagePage"},"relation":{"anyOf":[{"$ref":"#/components/schemas/RelationInfo"},{"type":"null"}],"description":"How this agent stands with this person, as the conversation opens.\n\n**Null when the agent has `relation_enabled` off**, which is not the same as \"they have no history\": the counters run for every pair regardless, and returning one for an agent that keeps none would show a relationship the model is not being told about.\n\nIt belongs to the **person**, not the conversation, so it carries across every session with the same `end_user_id`. Here so that rendering it at conversation start needs no second call; `GET /v1/agent/relations` is still how you read it any other time, and the socket's `relation` event is how you learn it moved mid-turn — which is rare, and is the only time it changes."},"resumed":{"type":"boolean","description":"True when this is a conversation that already existed rather than a new one — see `resume` on the request.\n\nWorth branching on: a resumed session arrives with its `messages` already populated and its `turn_seq` past zero, so a client that assumed an empty transcript will render nothing until the next turn."}},"required":["agent_id","created_at","end_user_id","id","title","turn_seq","updated_at","messages","relation","resumed"],"description":"A conversation, and a page of it. `messages` is the conversation so far, oldest first.\n\n**The most recent 50 by default**, not the whole transcript — a long conversation is not something a caller can be handed in one response. `messages.next_cursor` reaches the older ones: pass it as `cursor` on `GET /v1/agent/sessions/{session_id}`, whose `limit` raises or lowers the page, up to 200. It is null once the page reaches the beginning of the conversation.\n\n**`messages` is empty on a new conversation, including for an agent that has an opening line**. The opening line — generated or scripted — is the first turn on `/v1/agent/stream`, so it is written when a client connects rather than when the session is created. Opening a session makes no model call and no speech call of any kind."},"MessagePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MessageInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"MessageInfo":{"type":"object","properties":{"content":{"type":"string"},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"id":{"type":"string"},"knowledge":{"anyOf":[{"$ref":"#/components/schemas/MessageKnowledge"},{"type":"null"}],"description":"On a reply: what the agent looked up in its knowledge for it — each lookup's query, outcome and the entries it found, with their scores. Null when nothing was looked up."},"mood":{"type":["string","null"],"description":"How the agent said it felt when it wrote this reply, when the agent reports moods. Null on user messages, on agents with emotion off, and on any reply where the model did not report a usable one.","examples":["happy"]},"mood_intensity":{"type":["number","null"],"description":"How strongly, 0 to 1. Null when the model gave a label without a number — the label is the half it gets right."},"role":{"type":"string","enum":["user","assistant"],"examples":["assistant"]},"sources":{"type":"array","items":{"$ref":"#/components/schemas/MessageSource"},"description":"On a reply: the web pages it read, with the agent's `url_context` switch on. Empty when it read none, and on the person's messages."},"tool_calls":{"type":"array","items":{"$ref":"#/components/schemas/MessageToolCall"},"description":"On a reply: the calls of your tools it made, in order, each with its arguments and what it answered. Empty when it called none, and on the person's messages."}},"required":["content","created_at","id","knowledge","mood","mood_intensity","role","sources","tool_calls"]},"MessageKnowledge":{"type":"object","properties":{"lookups":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeLookupInfo"}},"mode":{"type":"string","enum":["lookup","prompt"],"description":"How the agent read its knowledge on this turn: `lookup` or `prompt`.","examples":["lookup"]}},"required":["lookups","mode"]},"KnowledgeLookupInfo":{"type":"object","properties":{"hits":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeCitation"}},"outcome":{"type":"string","enum":["found","none","failed"],"description":"`found`, `none` (nothing relevant enough), or `failed` (the lookup itself could not be done).","examples":["found"]},"query":{"type":"string"},"via":{"type":"string","enum":["first","tool"],"description":"`first` — looked up before the reply; `tool` — the model asked for it.","examples":["first"]}},"required":["hits","outcome","query","via"]},"KnowledgeCitation":{"type":"object","properties":{"entry_id":{"type":"string"},"score":{"type":"number"},"title":{"type":"string"}},"required":["entry_id","score","title"]},"MessageSource":{"type":"object","properties":{"url":{"type":"string","examples":["https://shop.example.com/returns"]},"retrieved":{"type":"boolean","description":"Whether the page could be read. A reply may name a page it could not fetch — behind a login, refused by its site, gone — and answers without it."}},"required":["url","retrieved"],"description":"A web page the reply read with the agent's `url_context` switch on."},"MessageToolCall":{"type":"object","properties":{"call_id":{"type":"string","description":"The call's id, `call_…`: what your server or app was sent."},"name":{"type":"string"},"kind":{"type":"string","enum":["webhook","app"]},"arguments":{"type":"object","additionalProperties":{},"description":"What the model called it with."},"result":{"description":"What it answered, as the model read it — its JSON, or its text, cut at the platform's `agents.tools.result_max_bytes` (`GET /v1/platform/limits`). Null when it answered nothing."},"ok":{"type":"boolean","description":"It answered in time, with a 2xx or a result."},"error":{"type":["string","null"],"description":"What failed, as the model was told — or, for a tool that asks first, that it waited for the person's yes. Null when nothing did."},"ms":{"type":"integer","description":"How long it took."},"round":{"type":"integer","description":"Which of the reply's rounds of calls, 1 to 3; 0 for a call the person confirmed with a tap."}},"required":["call_id","name","kind","arguments","ok","error","ms","round"]},"RelationInfo":{"type":"object","properties":{"agent_id":{"type":"string"},"agent_name":{"type":["string","null"],"description":"The agent's name, so a list of these is readable without resolving ids.","examples":["Aria"]},"conversations":{"type":"integer","description":"Separate sessions, ever. A stronger signal of familiarity than `turns` — twenty short visits is a longer acquaintance than one long conversation."},"disclosure":{"type":"number","description":"How much the agent has told **them** about itself, -1 to 1 — the asymmetric half of knowing each other, and the one nothing can count.\n\nDeliberately not part of `tier`: an agent can have opened up to somebody it has come to dislike, and folding this into affection would make that state unrepresentable."},"end_user_id":{"type":"string","description":"The person, as `eu_...`.","examples":["eu_7Fk2mQ9xLp4Rv1Nt"]},"first_met_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"last_seen_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"note":{"type":["string","null"],"description":"One line the model wrote about them. **Replaced, not appended** — it is rendered into every future prompt for this pair, so it is bounded by design."},"recorded":{"type":"integer","description":"How many times the model has recorded a change. Zero on a long history means the tool is not being called, which is the first thing to check when a relationship is not moving."},"summary":{"type":"string","description":"The relationship as the model is told it — bands and counted phrases, never numbers. **Empty string** when there is nothing worth saying yet, in which case nothing about them is added to the prompt at all — which is a real state and not a missing value."},"tier":{"type":"string","enum":["Despised","Disliked","Acquaintances","Friends","Good Friends"],"description":"What the agent would call this relationship, and what it is actually told: **Despised, Disliked, Acquaintances, Friends** or **Good Friends**.\n\nDerived on every read from the axes below and never stored, so it cannot go stale. The rule is one sentence: *warmth and trust set the direction, familiarity sets how far it can go* — a first conversation caps at Acquaintances however well it went, and the ceiling rises as they talk. Dislike is not capped: an agent can take against somebody on one remark.","examples":["Friends"]},"trust":{"type":"number","description":"-1 to 1, on the same scale."},"turns":{"type":"integer","description":"Turns exchanged, ever. Counted, not judged."},"warmth":{"type":"number","description":"-1 to 1. Zero is indifference, which is where every relationship starts."}},"required":["agent_id","agent_name","conversations","disclosure","end_user_id","first_met_at","last_seen_at","note","recorded","summary","tier","trust","turns","warmth"]},"SessionCreateRequest":{"type":"object","properties":{"agent_id":{"type":"string","description":"Which agent this conversation is with."},"end_user_id":{"type":["string","null"],"maxLength":128,"description":"Optional, and only ever the key's own end user — their id from `POST /v1/end-users`, or your own id for them.\n\n**A conversation is always the person the ephemeral key was minted for**: the key says who is talking, so this can only confirm it, and naming anybody else is a **403**. What the agent remembers about them — durable facts and the standing relationship — is theirs, and carries into every conversation with any of your agents.\n\nSet once, when the session opens. It cannot change afterwards.","examples":["eu_7Kq2mZp1XvA9dRt4"]},"resume":{"type":["boolean","null"],"description":"Whether to continue this person's most recent conversation with this agent instead of starting a new one.\n\n**Unset is the useful default**: it resumes when this person has a conversation with this agent young enough to continue — within the organisation's `resume_max_age_days`, else the platform's `agents.sessions.resume_max_age_days` (`GET /v1/platform/limits`) — and starts a new one otherwise. That matches what 'open a conversation with this person' usually means, and it stops an integration that opens a session per page load from scattering somebody's history across a dozen fragments.\n\n`false` always starts a new conversation — this is what a 'new chat' button should send. `true` is the same as leaving it unset; it exists so the intent can be stated rather than implied.\n\nNothing is lost by starting fresh: durable facts and the standing relationship belong to the *person*, not the conversation, and are carried into every new one.\n\nThe response's `resumed` says which happened."},"title":{"type":["string","null"],"maxLength":255,"description":"Optional label."}},"required":["agent_id"],"additionalProperties":false},"SessionPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SessionInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"SessionInfo":{"type":"object","properties":{"agent_id":{"type":"string"},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"end_user_id":{"type":"string","description":"Who this conversation is with. Send it again to open another conversation with the same person, carrying their memory.","examples":["eu_7Kq2mZp1XvA9dRt4"]},"id":{"type":"string"},"title":{"type":["string","null"]},"turn_seq":{"type":"integer"},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]}},"required":["agent_id","created_at","end_user_id","id","title","turn_seq","updated_at"]},"CompactionInfo":{"type":"object","properties":{"after_seq":{"type":"integer","description":"The last message this summary covers. Anything after it — the last few exchanges, kept word for word, then whatever is said next — is still sent to the model verbatim."},"facts_stored":{"type":"integer","description":"Durable facts written to long-term memory by this call. Zero when extraction was not asked for, or when there was nothing worth keeping."},"input_tokens":{"type":["integer","null"]},"model":{"type":["string","null"],"description":"What produced the summary."},"output_tokens":{"type":["integer","null"]},"session_id":{"type":"string"},"summary":{"type":"string","description":"What the next turn will start from."},"trigger":{"type":"string","enum":["manual","automatic","idle"],"description":"`manual` (a client asked) or `automatic` (a turn reached the agent's threshold, or a long compaction's next pass). `idle` is no longer written: it marks compactions made when a quiet conversation's facts were read, before facts were read on their own.","examples":["manual"]}},"required":["after_seq","facts_stored","input_tokens","model","output_tokens","session_id","summary","trigger"]},"CompactRequest":{"type":"object","properties":{"extract_facts":{"type":["boolean","null"],"description":"Whether to also extract durable facts about the end user, in the same model call.\n\nUnset uses the agent's own setting."}},"additionalProperties":false},"FactPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FactInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"FactInfo":{"type":"object","properties":{"agent_id":{"type":["string","null"],"description":"Which agent was being talked to when this was learned."},"confirmed_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When a conversation last said it again; `null` for never. The agents see the most recently confirmed first when not every fact fits."},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"id":{"type":"string"},"replaced_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When a later conversation replaced or removed it; `null` while it stands. Listed only with `include_replaced=true`."},"replaced_by":{"type":["string","null"],"description":"The fact that replaced it — moved city, changed job — or `null` when it was removed with nothing in its place, or stands."},"scope":{"type":"string","enum":["person","agent"],"description":"`person`: recalled by every agent of yours that shares its facts. `agent`: kept to one agent (`agent_id`), which alone recalls it — learnt by an agent whose `facts_scope` is `agent`, or added for that agent."},"source":{"type":"string","enum":["conversation","customer"],"description":"`conversation`: a reading learnt it. `customer`: you added or corrected it — a later conversation never replaces or removes it."},"text":{"type":"string"},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When it last changed: its `replaced_at` once replaced, else when you last corrected it, else its `created_at`."}},"required":["agent_id","confirmed_at","created_at","id","replaced_at","replaced_by","scope","source","text","updated_at"]},"FactCreateRequest":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":500,"description":"What to remember about the person, as one plain statement: \"Prefers to be called Sam.\" At most 500 characters. Never a secret — a password, a code, a card, account or identity number, or a key is refused, since facts are sent to the model on every turn.","examples":["Prefers to be called Sam."]},"agent_id":{"type":["string","null"],"description":"Keep it to one agent: only that agent recalls it. Unset or `null`: every agent of yours that shares its facts — and the ones that keep their own, too."}},"required":["text"],"additionalProperties":false},"FactUpdateRequest":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":500,"description":"What to remember about the person, as one plain statement: \"Prefers to be called Sam.\" At most 500 characters. Never a secret — a password, a code, a card, account or identity number, or a key is refused, since facts are sent to the model on every turn.","examples":["Prefers to be called Sam."]}},"required":["text"],"additionalProperties":false},"FactsDeleted":{"type":"object","properties":{"facts_deleted":{"type":"integer","description":"How many stored facts were deleted."}},"required":["facts_deleted"]},"RelationPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RelationInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"AgentPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentSummary"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"AgentSummary":{"type":"object","properties":{"code_execution":{"type":"boolean","description":"Whether the model may write and run Python while it replies — sums, unit conversions, dates — and answer from what it printed. Gemini's own tool: it runs at Google, with no network, and nothing of yours.\n\n**No fee, but not free**: the code and its output are model tokens, billed like any reply."},"compact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What to tell the model when summarising. Unset uses the organisation's default, then a built-in instruction written for in-character conversations."},"compact_max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Length cap on the summary, in tokens — the model's thinking included, which shares the budget. Unset uses the platform's `agents.memory.compaction.max_output_tokens` on the Limits page, twice it when the compaction extracts facts too."},"compact_model":{"type":["string","null"],"maxLength":64,"description":"Model for the summarising call. Unset uses the agent's own."},"compact_thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"Thinking level for the summarising call."},"compact_threshold_tokens":{"type":["integer","null"],"minimum":1,"maximum":200000,"description":"Compact automatically once a reply's first model call reads this many tokens — the conversation, its summary and the instruction, without the inline knowledge or the turn's looked-up passages, which no summary shrinks. Tool results do not count. Unset uses the platform's default, `agents.memory.compaction.default_threshold_tokens` on the Limits page. At most 200,000, and at least twice what the instruction costs on its own. `POST /v1/agent/sessions/{id}/compact` works whatever it is."},"emotion_enabled":{"type":"boolean","description":"Whether the model reports its own mood — one line in front of each reply, which is taken off the reply and sent as the socket's `mood` event, and kept on each stored message.\n\nCosts about six output tokens per turn and no extra model call. The reply text you are handed does not contain the line: a marker with a known label, or with an intensity, or alone on its own line is always removed, and only a marker with an unknown label, no intensity and prose on the same line is left — at that point it cannot be told from a sentence that happens to start with the word."},"emotion_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about reporting a mood. Unset uses the organisation's default, then a built-in one naming every supported label.\n\n**Override this and you own the format.** The gateway strips a first line shaped `mood: <label> <intensity>` — with a label it knows, or with an intensity, or alone on its line — and records a mood only for a label it knows. An instruction that asks for any other shape leaves the model's line in the reply as text and stores no mood."},"environment":{"type":["string","null"],"maxLength":8000,"description":"The setting the conversation takes place in."},"extract_facts_on_compact":{"type":"boolean","description":"Whether a compaction also extracts durable facts about the user, in the same model call. A caller can always override this per request; this is what the automatic path uses."},"fact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What counts as worth remembering. Unset falls back to the organisation's default, then to a built-in instruction."},"facts_enabled":{"type":"boolean","description":"Whether this agent remembers facts about the people it talks to. **On by default**, unlike most switches here.\n\nOff, its conversations are never read for facts and its turns recall none — nothing is charged for either — and a compaction extracts nothing whatever it is asked. Facts already stored stay, and other agents still recall the ones they share. Turning it back on reads only what is said from then on."},"facts_scope":{"type":["string","null"],"enum":["person","agent",null],"description":"Whose facts this agent sees and adds to. `person` (the default, and what unset means): what any of your agents learnt about the person that shares. `agent`: its own only — what it learns is not recalled by your other agents, and it does not recall theirs.\n\nA change applies to what it learns from then on: a fact keeps the scope it was learnt under.","examples":["person"]},"first_message":{"type":["string","null"],"maxLength":4000,"description":"The agent's opening line — or, with `first_message_generated` on, the direction for one.\n\nAs a line it costs no tokens and is identical every time. As a direction (\"say hello and catch up with them\") the model writes it fresh, so it can use whatever long-term memory recalled about that person — at the cost of one billed model call.\n\n**Either way it is delivered as the agent's first turn on `/v1/agent/stream`**, so it is streamed and spoken a sentence at a time like every other reply. Opening a session with `POST /v1/agent/sessions` does not produce it, and an integration that never opens a socket never hears it.\n\nThe agent is not greeted twice: a conversation continued within a few minutes gets no fresh opening line, and `ready.greeting` says which before a frame arrives."},"first_message_generated":{"type":"boolean","description":"Whether `first_message` is a direction for the model rather than the words themselves.\n\n**The same text means different things under this flag**, and nothing on the server can tell them apart. Turning it off while the text is still written as an instruction shows that instruction to your users verbatim.\n\nIt needs a `first_message` to act on: switched on with the text empty is refused (422), because an agent with a direction to follow and no direction never greets."},"goal":{"type":["string","null"],"maxLength":8000,"description":"What it is trying to achieve in the conversation."},"guardrails":{"type":["string","null"],"maxLength":8000,"description":"What it must not do or discuss."},"knowledge_enabled":{"type":"boolean","description":"Whether the agent may use the knowledge entries assigned to it. **Defaults to on**, unlike the other switches here: knowledge works as soon as an entry is assigned, and this only takes it away again.\n\nOff, the entries are not listed in the system instruction and the `search_knowledge` tool is not declared, so the agent answers as though it had none. **Nothing is deleted or unassigned** — turning it back on restores exactly what was there, which is what makes this the way to try an agent without its reference material.\n\nAssign entries with `PUT /v1/agents/{agent_id}/knowledge`."},"knowledge_lookup_first":{"type":["boolean","null"],"description":"When knowledge is looked up: search before every reply and hand the model what matches, so it never answers from memory what its material says. **On by default.** Off, the model searches only when it decides to, which is slower when it does and wrong more often when it does not."},"knowledge_max_results":{"type":["integer","null"],"minimum":1,"maximum":8,"description":"Pieces of material a lookup hands the model, 1-8. Unset is the platform's `agents.knowledge.default_results` (`GET /v1/platform/limits`)."},"knowledge_mode":{"type":["string","null"],"enum":["auto","prompt","lookup",null],"description":"How the agent reads its knowledge. `auto` (the default, and what unset means) puts the whole text in the prompt while it fits the platform's `agents.knowledge.inline_budget_tokens` — no lookup, nothing to miss — and looks it up beyond that. `prompt` puts it in the prompt up to the platform's `agents.knowledge.prompt_cap_tokens` (each turn's first word is slower the more there is). `lookup` always looks it up. Both are on `GET /v1/platform/limits`.","examples":["auto"]},"max_facts":{"type":["integer","null"],"minimum":1,"maximum":100,"description":"The most of the person's facts put in front of the model, 1-100. Unset is as many as fit in the platform's `agents.memory.recall.budget_chars` (`GET /v1/platform/limits`; 6,000 characters is some 1,500 tokens). When they do not all fit, the ones a conversation confirmed or learnt most recently go in. They are prompt tokens on every turn."},"max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Cap on reply length. Unset means the model's own limit."},"model":{"type":"string","description":"Gemini model id.","examples":["gemini-3.1-flash-lite"]},"name":{"type":"string","description":"Human-readable name. Shown in the console; never sent to the model."},"personality":{"type":["string","null"],"maxLength":8000,"description":"Who the agent is — character, background, quirks."},"relation_enabled":{"type":"boolean","description":"Whether the agent keeps a standing relationship with each person it talks to — warmth, trust and how well they know each other, carried across every conversation with that end user and never forgotten.\n\nTurning this on declares a tool the model calls when something changes its view of them. **It adds no latency to an ordinary turn**: the provider returns the reply and the tool call together, and the call is answered on the next turn. The one exception is a reply that is only the call with no words in it, which costs a second model call to write them.\n\nRead it with `GET /v1/agent/relations`."},"relation_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about the relationship it keeps. Unset uses the organisation's default, then a built-in one.\n\nThis is the *disposition* — that the history is real and that most replies should record nothing. The tool's own description covers the mechanism, and is not configurable."},"stream_chunk_schedule":{"type":["array","null"],"items":{"type":"integer","minimum":1,"maximum":2000},"minItems":1,"maxItems":8,"description":"How a streamed reply is cut into pieces for speech, smallest first - for example `[60, 160, 250]`. Ascending; a schedule out of order is refused.\n\n**Only `/v1/agent/stream` uses this**; a buffered turn has the whole reply before it speaks and nothing to schedule.\n\nThe **first** number is the one that matters: it decides how much text must arrive before the agent starts talking, so lowering it starts speech sooner. There is a floor worth knowing - a chunk has to be generated faster than the previous one plays, so a first chunk under roughly 50 characters starts sooner and then runs out of audio before the next piece exists, which is heard as a stutter rather than as a fast reply. Later numbers trade first-audio latency for prosody: a longer piece is spoken with better intonation because the model can see more of the sentence at once.\n\nOmit it to use your organisation's default, and then the platform's."},"stt_config":{"anyOf":[{"$ref":"#/components/schemas/AgentSTTInfo"},{"type":"null"}],"description":"How a spoken turn is transcribed — the model, the language hint, and the sample rate a client sends PCM at on `/v1/agent/stream`. Only what you set is stored: a model left unset follows the platform's default.\n\n**Defaults, not policy.** `user_audio.start` may name any of these per turn and what it names wins. There is no switch for hearing, and no scope of its own: a credential that may talk to an agent may speak to it, exactly as it has always been able to hear it speak back."},"thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"How hard the model thinks before answering. `minimal` is fastest to first token; `medium` is the model's own default. Unset means the model decides."},"thinking_summaries":{"type":["string","null"],"enum":["auto","none",null],"description":"Whether a readable summary of the reasoning is returned."},"tone":{"type":["string","null"],"maxLength":8000,"description":"How it speaks: register, pacing, verbosity."},"url_context":{"type":"boolean","description":"Whether the model may read a web page by its address while it replies, when the person gives one or one is in its instructions. Gemini's own tool: the page is fetched by Google. The pages it read come back as the reply's `sources`.\n\n**No fee, but not free**: a page's text is model tokens, billed like any reply — a long page can be thousands. A page is untrusted input: what it says reaches the model, so the most it can steer the agent towards is the agent's own tools, and a tool marked `ask_first` still needs the person."},"voice_config":{"anyOf":[{"$ref":"#/components/schemas/AgentVoiceInfo"},{"type":"null"}],"description":"Voice and model settings.\n\n**A speaking agent speaks on `eleven_flash_v2_5`** (used when `model` is left unset) **or `eleven_multilingual_v2`**, with any voice from `GET /v1/voices` that offers it. `eleven_v3` is refused with 422 when `voice_enabled` is true: it cannot stream over the socket agents use. An agent that does not speak may leave this unset."},"voice_enabled":{"type":"boolean","description":"Whether replies come back as audio as well as text.\n\nSpeech is generated after the reply and adds its own latency — roughly a fifth of the spoken duration. A caller can override this per turn with `speech` on the socket's `user_message` or `user_audio.start`."},"id":{"type":"string"},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"version":{"type":"integer","description":"Moves by one on every save. Send it back as `expected_version` on an update to have the save refused (409) if somebody else changed the agent since you read it."},"sessions":{"type":"integer","description":"Conversations opened with this agent."},"turns":{"type":"integer","description":"Completed turns across all of them."},"last_used_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When a turn last completed. Null means no conversation with this agent has ever got past being opened — which is not the same as having none."}},"required":["code_execution","compact_instruction","compact_max_output_tokens","compact_model","compact_thinking_level","compact_threshold_tokens","emotion_enabled","emotion_instruction","environment","extract_facts_on_compact","fact_instruction","facts_enabled","facts_scope","first_message","first_message_generated","goal","guardrails","knowledge_enabled","knowledge_lookup_first","knowledge_max_results","knowledge_mode","max_facts","max_output_tokens","model","name","personality","relation_enabled","relation_instruction","stream_chunk_schedule","stt_config","thinking_level","thinking_summaries","tone","url_context","voice_config","voice_enabled","id","created_at","updated_at","version","sessions","turns","last_used_at"]},"AgentSTTInfo":{"type":"object","properties":{"language":{"type":["string","null"],"description":"ISO-639-1 or -639-3 code the agent's spoken turns are transcribed in. Omit it and the language is detected per turn. A `language` on `user_audio.start` overrides it.","examples":["en","fa","de"]},"mark_spoken":{"type":["boolean","null"],"description":"Whether a spoken turn reaches the model with a note saying it was transcribed from speech and may contain recognition errors, so the agent reads it for meaning rather than answering a misheard word or pointing out a typo nobody made. Typed turns are never marked, and the note is not stored in the transcript. Unset or null means on."},"model":{"type":["string","null"],"enum":["scribe_v2",null],"description":"Transcription model for this agent's spoken turns. Unset or null means the platform's default model, and follows it if that default ever changes."},"sample_rate":{"type":["integer","null"],"enum":[8000,16000,22050,24000,44100,null],"description":"Hz of the PCM a client sends on `/v1/agent/stream`. One of 8000, 16000, 22050, 24000, 44100.\n\n**Only the socket uses this.** Every other way of sending audio sends a file, and a file states its own rate.\n\nUnset means 24000, which is what the socket *sends* — so a client that records what it plays needs nothing here. 16000 is a third less upstream for audio the transcriber resamples to 16000 anyway, which is the reason to change it. Announced back on `ready` either way.","examples":[16000,24000]}},"required":["language","mark_spoken","model","sample_rate"]},"AgentVoiceInfo":{"type":"object","properties":{"format":{"type":["string","null"],"enum":["wav","pcm",null],"description":"The audio format this voice is configured with. On the WebSocket a reply is always raw 16-bit PCM, at the rate its `audio.start` names."},"model":{"type":["string","null"],"description":"Which model speaks: `eleven_flash_v2_5` (the default) or `eleven_multilingual_v2`, and one the voice lists as `models` in `GET /v1/voices`."},"provider_options":{"anyOf":[{"$ref":"#/components/schemas/ProviderVoiceSettingsInfo"},{"type":"null"}],"description":"The provider's voice settings, on top of the ones the voice was registered with. Unset takes theirs."},"sample_rate":{"type":["integer","null"],"enum":[8000,16000,22050,24000,44100,null],"description":"Hz of the returned audio — one of 8000, 16000, 22050, 24000, 44100, each served natively by the provider. Unset means 24000.\n\nThe response's `sample_rate` and the `X-Sample-Rate` header always say what was actually produced."},"voice_id":{"type":["string","null"],"description":"An id from `GET /v1/voices` — the built-in voice, a platform voice, or one your organisation uploaded."}},"required":["format","model","provider_options","sample_rate","voice_id"]},"AgentInfo":{"type":"object","properties":{"code_execution":{"type":"boolean","description":"Whether the model may write and run Python while it replies — sums, unit conversions, dates — and answer from what it printed. Gemini's own tool: it runs at Google, with no network, and nothing of yours.\n\n**No fee, but not free**: the code and its output are model tokens, billed like any reply."},"compact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What to tell the model when summarising. Unset uses the organisation's default, then a built-in instruction written for in-character conversations."},"compact_max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Length cap on the summary, in tokens — the model's thinking included, which shares the budget. Unset uses the platform's `agents.memory.compaction.max_output_tokens` on the Limits page, twice it when the compaction extracts facts too."},"compact_model":{"type":["string","null"],"maxLength":64,"description":"Model for the summarising call. Unset uses the agent's own."},"compact_thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"Thinking level for the summarising call."},"compact_threshold_tokens":{"type":["integer","null"],"minimum":1,"maximum":200000,"description":"Compact automatically once a reply's first model call reads this many tokens — the conversation, its summary and the instruction, without the inline knowledge or the turn's looked-up passages, which no summary shrinks. Tool results do not count. Unset uses the platform's default, `agents.memory.compaction.default_threshold_tokens` on the Limits page. At most 200,000, and at least twice what the instruction costs on its own. `POST /v1/agent/sessions/{id}/compact` works whatever it is."},"emotion_enabled":{"type":"boolean","description":"Whether the model reports its own mood — one line in front of each reply, which is taken off the reply and sent as the socket's `mood` event, and kept on each stored message.\n\nCosts about six output tokens per turn and no extra model call. The reply text you are handed does not contain the line: a marker with a known label, or with an intensity, or alone on its own line is always removed, and only a marker with an unknown label, no intensity and prose on the same line is left — at that point it cannot be told from a sentence that happens to start with the word."},"emotion_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about reporting a mood. Unset uses the organisation's default, then a built-in one naming every supported label.\n\n**Override this and you own the format.** The gateway strips a first line shaped `mood: <label> <intensity>` — with a label it knows, or with an intensity, or alone on its line — and records a mood only for a label it knows. An instruction that asks for any other shape leaves the model's line in the reply as text and stores no mood."},"environment":{"type":["string","null"],"maxLength":8000,"description":"The setting the conversation takes place in."},"extract_facts_on_compact":{"type":"boolean","description":"Whether a compaction also extracts durable facts about the user, in the same model call. A caller can always override this per request; this is what the automatic path uses."},"fact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What counts as worth remembering. Unset falls back to the organisation's default, then to a built-in instruction."},"facts_enabled":{"type":"boolean","description":"Whether this agent remembers facts about the people it talks to. **On by default**, unlike most switches here.\n\nOff, its conversations are never read for facts and its turns recall none — nothing is charged for either — and a compaction extracts nothing whatever it is asked. Facts already stored stay, and other agents still recall the ones they share. Turning it back on reads only what is said from then on."},"facts_scope":{"type":["string","null"],"enum":["person","agent",null],"description":"Whose facts this agent sees and adds to. `person` (the default, and what unset means): what any of your agents learnt about the person that shares. `agent`: its own only — what it learns is not recalled by your other agents, and it does not recall theirs.\n\nA change applies to what it learns from then on: a fact keeps the scope it was learnt under.","examples":["person"]},"first_message":{"type":["string","null"],"maxLength":4000,"description":"The agent's opening line — or, with `first_message_generated` on, the direction for one.\n\nAs a line it costs no tokens and is identical every time. As a direction (\"say hello and catch up with them\") the model writes it fresh, so it can use whatever long-term memory recalled about that person — at the cost of one billed model call.\n\n**Either way it is delivered as the agent's first turn on `/v1/agent/stream`**, so it is streamed and spoken a sentence at a time like every other reply. Opening a session with `POST /v1/agent/sessions` does not produce it, and an integration that never opens a socket never hears it.\n\nThe agent is not greeted twice: a conversation continued within a few minutes gets no fresh opening line, and `ready.greeting` says which before a frame arrives."},"first_message_generated":{"type":"boolean","description":"Whether `first_message` is a direction for the model rather than the words themselves.\n\n**The same text means different things under this flag**, and nothing on the server can tell them apart. Turning it off while the text is still written as an instruction shows that instruction to your users verbatim.\n\nIt needs a `first_message` to act on: switched on with the text empty is refused (422), because an agent with a direction to follow and no direction never greets."},"goal":{"type":["string","null"],"maxLength":8000,"description":"What it is trying to achieve in the conversation."},"guardrails":{"type":["string","null"],"maxLength":8000,"description":"What it must not do or discuss."},"knowledge_enabled":{"type":"boolean","description":"Whether the agent may use the knowledge entries assigned to it. **Defaults to on**, unlike the other switches here: knowledge works as soon as an entry is assigned, and this only takes it away again.\n\nOff, the entries are not listed in the system instruction and the `search_knowledge` tool is not declared, so the agent answers as though it had none. **Nothing is deleted or unassigned** — turning it back on restores exactly what was there, which is what makes this the way to try an agent without its reference material.\n\nAssign entries with `PUT /v1/agents/{agent_id}/knowledge`."},"knowledge_lookup_first":{"type":["boolean","null"],"description":"When knowledge is looked up: search before every reply and hand the model what matches, so it never answers from memory what its material says. **On by default.** Off, the model searches only when it decides to, which is slower when it does and wrong more often when it does not."},"knowledge_max_results":{"type":["integer","null"],"minimum":1,"maximum":8,"description":"Pieces of material a lookup hands the model, 1-8. Unset is the platform's `agents.knowledge.default_results` (`GET /v1/platform/limits`)."},"knowledge_mode":{"type":["string","null"],"enum":["auto","prompt","lookup",null],"description":"How the agent reads its knowledge. `auto` (the default, and what unset means) puts the whole text in the prompt while it fits the platform's `agents.knowledge.inline_budget_tokens` — no lookup, nothing to miss — and looks it up beyond that. `prompt` puts it in the prompt up to the platform's `agents.knowledge.prompt_cap_tokens` (each turn's first word is slower the more there is). `lookup` always looks it up. Both are on `GET /v1/platform/limits`.","examples":["auto"]},"max_facts":{"type":["integer","null"],"minimum":1,"maximum":100,"description":"The most of the person's facts put in front of the model, 1-100. Unset is as many as fit in the platform's `agents.memory.recall.budget_chars` (`GET /v1/platform/limits`; 6,000 characters is some 1,500 tokens). When they do not all fit, the ones a conversation confirmed or learnt most recently go in. They are prompt tokens on every turn."},"max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Cap on reply length. Unset means the model's own limit."},"model":{"type":"string","description":"Gemini model id.","examples":["gemini-3.1-flash-lite"]},"name":{"type":"string","description":"Human-readable name. Shown in the console; never sent to the model."},"personality":{"type":["string","null"],"maxLength":8000,"description":"Who the agent is — character, background, quirks."},"relation_enabled":{"type":"boolean","description":"Whether the agent keeps a standing relationship with each person it talks to — warmth, trust and how well they know each other, carried across every conversation with that end user and never forgotten.\n\nTurning this on declares a tool the model calls when something changes its view of them. **It adds no latency to an ordinary turn**: the provider returns the reply and the tool call together, and the call is answered on the next turn. The one exception is a reply that is only the call with no words in it, which costs a second model call to write them.\n\nRead it with `GET /v1/agent/relations`."},"relation_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about the relationship it keeps. Unset uses the organisation's default, then a built-in one.\n\nThis is the *disposition* — that the history is real and that most replies should record nothing. The tool's own description covers the mechanism, and is not configurable."},"stream_chunk_schedule":{"type":["array","null"],"items":{"type":"integer","minimum":1,"maximum":2000},"minItems":1,"maxItems":8,"description":"How a streamed reply is cut into pieces for speech, smallest first - for example `[60, 160, 250]`. Ascending; a schedule out of order is refused.\n\n**Only `/v1/agent/stream` uses this**; a buffered turn has the whole reply before it speaks and nothing to schedule.\n\nThe **first** number is the one that matters: it decides how much text must arrive before the agent starts talking, so lowering it starts speech sooner. There is a floor worth knowing - a chunk has to be generated faster than the previous one plays, so a first chunk under roughly 50 characters starts sooner and then runs out of audio before the next piece exists, which is heard as a stutter rather than as a fast reply. Later numbers trade first-audio latency for prosody: a longer piece is spoken with better intonation because the model can see more of the sentence at once.\n\nOmit it to use your organisation's default, and then the platform's."},"stt_config":{"anyOf":[{"$ref":"#/components/schemas/AgentSTTInfo"},{"type":"null"}],"description":"How a spoken turn is transcribed — the model, the language hint, and the sample rate a client sends PCM at on `/v1/agent/stream`. Only what you set is stored: a model left unset follows the platform's default.\n\n**Defaults, not policy.** `user_audio.start` may name any of these per turn and what it names wins. There is no switch for hearing, and no scope of its own: a credential that may talk to an agent may speak to it, exactly as it has always been able to hear it speak back."},"thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"How hard the model thinks before answering. `minimal` is fastest to first token; `medium` is the model's own default. Unset means the model decides."},"thinking_summaries":{"type":["string","null"],"enum":["auto","none",null],"description":"Whether a readable summary of the reasoning is returned."},"tone":{"type":["string","null"],"maxLength":8000,"description":"How it speaks: register, pacing, verbosity."},"url_context":{"type":"boolean","description":"Whether the model may read a web page by its address while it replies, when the person gives one or one is in its instructions. Gemini's own tool: the page is fetched by Google. The pages it read come back as the reply's `sources`.\n\n**No fee, but not free**: a page's text is model tokens, billed like any reply — a long page can be thousands. A page is untrusted input: what it says reaches the model, so the most it can steer the agent towards is the agent's own tools, and a tool marked `ask_first` still needs the person."},"voice_config":{"anyOf":[{"$ref":"#/components/schemas/AgentVoiceInfo"},{"type":"null"}],"description":"Voice and model settings.\n\n**A speaking agent speaks on `eleven_flash_v2_5`** (used when `model` is left unset) **or `eleven_multilingual_v2`**, with any voice from `GET /v1/voices` that offers it. `eleven_v3` is refused with 422 when `voice_enabled` is true: it cannot stream over the socket agents use. An agent that does not speak may leave this unset."},"voice_enabled":{"type":"boolean","description":"Whether replies come back as audio as well as text.\n\nSpeech is generated after the reply and adds its own latency — roughly a fifth of the spoken duration. A caller can override this per turn with `speech` on the socket's `user_message` or `user_audio.start`."},"id":{"type":"string"},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"version":{"type":"integer","description":"Moves by one on every save. Send it back as `expected_version` on an update to have the save refused (409) if somebody else changed the agent since you read it."}},"required":["code_execution","compact_instruction","compact_max_output_tokens","compact_model","compact_thinking_level","compact_threshold_tokens","emotion_enabled","emotion_instruction","environment","extract_facts_on_compact","fact_instruction","facts_enabled","facts_scope","first_message","first_message_generated","goal","guardrails","knowledge_enabled","knowledge_lookup_first","knowledge_max_results","knowledge_mode","max_facts","max_output_tokens","model","name","personality","relation_enabled","relation_instruction","stream_chunk_schedule","stt_config","thinking_level","thinking_summaries","tone","url_context","voice_config","voice_enabled","id","created_at","updated_at","version"]},"AgentCreateRequest":{"type":"object","properties":{"code_execution":{"type":["boolean","null"],"description":"Whether the model may write and run Python while it replies — sums, unit conversions, dates — and answer from what it printed. Gemini's own tool: it runs at Google, with no network, and nothing of yours.\n\n**No fee, but not free**: the code and its output are model tokens, billed like any reply."},"compact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What to tell the model when summarising. Unset uses the organisation's default, then a built-in instruction written for in-character conversations."},"compact_max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Length cap on the summary, in tokens — the model's thinking included, which shares the budget. Unset uses the platform's `agents.memory.compaction.max_output_tokens` on the Limits page, twice it when the compaction extracts facts too."},"compact_model":{"type":["string","null"],"maxLength":64,"description":"Model for the summarising call. Unset uses the agent's own."},"compact_thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"Thinking level for the summarising call."},"compact_threshold_tokens":{"type":["integer","null"],"minimum":1,"maximum":200000,"description":"Compact automatically once a reply's first model call reads this many tokens — the conversation, its summary and the instruction, without the inline knowledge or the turn's looked-up passages, which no summary shrinks. Tool results do not count. Unset uses the platform's default, `agents.memory.compaction.default_threshold_tokens` on the Limits page. At most 200,000, and at least twice what the instruction costs on its own. `POST /v1/agent/sessions/{id}/compact` works whatever it is."},"emotion_enabled":{"type":["boolean","null"],"description":"Whether the model reports its own mood — one line in front of each reply, which is taken off the reply and sent as the socket's `mood` event, and kept on each stored message.\n\nCosts about six output tokens per turn and no extra model call. The reply text you are handed does not contain the line: a marker with a known label, or with an intensity, or alone on its own line is always removed, and only a marker with an unknown label, no intensity and prose on the same line is left — at that point it cannot be told from a sentence that happens to start with the word."},"emotion_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about reporting a mood. Unset uses the organisation's default, then a built-in one naming every supported label.\n\n**Override this and you own the format.** The gateway strips a first line shaped `mood: <label> <intensity>` — with a label it knows, or with an intensity, or alone on its line — and records a mood only for a label it knows. An instruction that asks for any other shape leaves the model's line in the reply as text and stores no mood."},"environment":{"type":["string","null"],"maxLength":8000,"description":"The setting the conversation takes place in."},"extract_facts_on_compact":{"type":["boolean","null"],"description":"Whether a compaction also extracts durable facts about the user, in the same model call. A caller can always override this per request; this is what the automatic path uses."},"fact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What counts as worth remembering. Unset falls back to the organisation's default, then to a built-in instruction."},"facts_enabled":{"type":["boolean","null"],"description":"Whether this agent remembers facts about the people it talks to. **On by default**, unlike most switches here.\n\nOff, its conversations are never read for facts and its turns recall none — nothing is charged for either — and a compaction extracts nothing whatever it is asked. Facts already stored stay, and other agents still recall the ones they share. Turning it back on reads only what is said from then on."},"facts_scope":{"type":["string","null"],"enum":["person","agent",null],"description":"Whose facts this agent sees and adds to. `person` (the default, and what unset means): what any of your agents learnt about the person that shares. `agent`: its own only — what it learns is not recalled by your other agents, and it does not recall theirs.\n\nA change applies to what it learns from then on: a fact keeps the scope it was learnt under.","examples":["person"]},"first_message":{"type":["string","null"],"maxLength":4000,"description":"The agent's opening line — or, with `first_message_generated` on, the direction for one.\n\nAs a line it costs no tokens and is identical every time. As a direction (\"say hello and catch up with them\") the model writes it fresh, so it can use whatever long-term memory recalled about that person — at the cost of one billed model call.\n\n**Either way it is delivered as the agent's first turn on `/v1/agent/stream`**, so it is streamed and spoken a sentence at a time like every other reply. Opening a session with `POST /v1/agent/sessions` does not produce it, and an integration that never opens a socket never hears it.\n\nThe agent is not greeted twice: a conversation continued within a few minutes gets no fresh opening line, and `ready.greeting` says which before a frame arrives."},"first_message_generated":{"type":["boolean","null"],"description":"Whether `first_message` is a direction for the model rather than the words themselves.\n\n**The same text means different things under this flag**, and nothing on the server can tell them apart. Turning it off while the text is still written as an instruction shows that instruction to your users verbatim.\n\nIt needs a `first_message` to act on: switched on with the text empty is refused (422), because an agent with a direction to follow and no direction never greets."},"goal":{"type":["string","null"],"maxLength":8000,"description":"What it is trying to achieve in the conversation."},"guardrails":{"type":["string","null"],"maxLength":8000,"description":"What it must not do or discuss."},"knowledge_enabled":{"type":["boolean","null"],"description":"Whether the agent may use the knowledge entries assigned to it. **Defaults to on**, unlike the other switches here: knowledge works as soon as an entry is assigned, and this only takes it away again.\n\nOff, the entries are not listed in the system instruction and the `search_knowledge` tool is not declared, so the agent answers as though it had none. **Nothing is deleted or unassigned** — turning it back on restores exactly what was there, which is what makes this the way to try an agent without its reference material.\n\nAssign entries with `PUT /v1/agents/{agent_id}/knowledge`."},"knowledge_lookup_first":{"type":["boolean","null"],"description":"When knowledge is looked up: search before every reply and hand the model what matches, so it never answers from memory what its material says. **On by default.** Off, the model searches only when it decides to, which is slower when it does and wrong more often when it does not."},"knowledge_max_results":{"type":["integer","null"],"minimum":1,"maximum":8,"description":"Pieces of material a lookup hands the model, 1-8. Unset is the platform's `agents.knowledge.default_results` (`GET /v1/platform/limits`)."},"knowledge_mode":{"type":["string","null"],"enum":["auto","prompt","lookup",null],"description":"How the agent reads its knowledge. `auto` (the default, and what unset means) puts the whole text in the prompt while it fits the platform's `agents.knowledge.inline_budget_tokens` — no lookup, nothing to miss — and looks it up beyond that. `prompt` puts it in the prompt up to the platform's `agents.knowledge.prompt_cap_tokens` (each turn's first word is slower the more there is). `lookup` always looks it up. Both are on `GET /v1/platform/limits`.","examples":["auto"]},"max_facts":{"type":["integer","null"],"minimum":1,"maximum":100,"description":"The most of the person's facts put in front of the model, 1-100. Unset is as many as fit in the platform's `agents.memory.recall.budget_chars` (`GET /v1/platform/limits`; 6,000 characters is some 1,500 tokens). When they do not all fit, the ones a conversation confirmed or learnt most recently go in. They are prompt tokens on every turn."},"max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Cap on reply length. Unset means the model's own limit."},"model":{"type":["string","null"],"maxLength":64,"description":"Gemini model id. Defaults to the platform's `agents.model` (`GET /v1/platform/limits`).","examples":["gemini-3.1-flash-lite"]},"name":{"type":"string","minLength":1,"maxLength":255,"description":"Human-readable name. Shown in the console; never sent to the model."},"personality":{"type":["string","null"],"maxLength":8000,"description":"Who the agent is — character, background, quirks."},"relation_enabled":{"type":["boolean","null"],"description":"Whether the agent keeps a standing relationship with each person it talks to — warmth, trust and how well they know each other, carried across every conversation with that end user and never forgotten.\n\nTurning this on declares a tool the model calls when something changes its view of them. **It adds no latency to an ordinary turn**: the provider returns the reply and the tool call together, and the call is answered on the next turn. The one exception is a reply that is only the call with no words in it, which costs a second model call to write them.\n\nRead it with `GET /v1/agent/relations`."},"relation_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about the relationship it keeps. Unset uses the organisation's default, then a built-in one.\n\nThis is the *disposition* — that the history is real and that most replies should record nothing. The tool's own description covers the mechanism, and is not configurable."},"stream_chunk_schedule":{"type":["array","null"],"items":{"type":"integer","minimum":1,"maximum":2000},"minItems":1,"maxItems":8,"description":"How a streamed reply is cut into pieces for speech, smallest first - for example `[60, 160, 250]`. Ascending; a schedule out of order is refused.\n\n**Only `/v1/agent/stream` uses this**; a buffered turn has the whole reply before it speaks and nothing to schedule.\n\nThe **first** number is the one that matters: it decides how much text must arrive before the agent starts talking, so lowering it starts speech sooner. There is a floor worth knowing - a chunk has to be generated faster than the previous one plays, so a first chunk under roughly 50 characters starts sooner and then runs out of audio before the next piece exists, which is heard as a stutter rather than as a fast reply. Later numbers trade first-audio latency for prosody: a longer piece is spoken with better intonation because the model can see more of the sentence at once.\n\nOmit it to use your organisation's default, and then the platform's."},"stt_config":{"anyOf":[{"$ref":"#/components/schemas/AgentSTT"},{"type":"null"}],"description":"How a spoken turn is transcribed — the model, the language hint, and the sample rate a client sends PCM at on `/v1/agent/stream`. Only what you set is stored: a model left unset follows the platform's default.\n\n**Defaults, not policy.** `user_audio.start` may name any of these per turn and what it names wins. There is no switch for hearing, and no scope of its own: a credential that may talk to an agent may speak to it, exactly as it has always been able to hear it speak back."},"thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"How hard the model thinks before answering. `minimal` is fastest to first token; `medium` is the model's own default. Unset means the model decides."},"thinking_summaries":{"type":["string","null"],"enum":["auto","none",null],"description":"Whether a readable summary of the reasoning is returned."},"tone":{"type":["string","null"],"maxLength":8000,"description":"How it speaks: register, pacing, verbosity."},"url_context":{"type":["boolean","null"],"description":"Whether the model may read a web page by its address while it replies, when the person gives one or one is in its instructions. Gemini's own tool: the page is fetched by Google. The pages it read come back as the reply's `sources`.\n\n**No fee, but not free**: a page's text is model tokens, billed like any reply — a long page can be thousands. A page is untrusted input: what it says reaches the model, so the most it can steer the agent towards is the agent's own tools, and a tool marked `ask_first` still needs the person."},"voice_config":{"anyOf":[{"$ref":"#/components/schemas/AgentVoice"},{"type":"null"}],"description":"Voice and model settings.\n\n**A speaking agent speaks on `eleven_flash_v2_5`** (used when `model` is left unset) **or `eleven_multilingual_v2`**, with any voice from `GET /v1/voices` that offers it. `eleven_v3` is refused with 422 when `voice_enabled` is true: it cannot stream over the socket agents use. An agent that does not speak may leave this unset."},"voice_enabled":{"type":["boolean","null"],"description":"Whether replies come back as audio as well as text.\n\nSpeech is generated after the reply and adds its own latency — roughly a fifth of the spoken duration. A caller can override this per turn with `speech` on the socket's `user_message` or `user_audio.start`."}},"required":["name"],"additionalProperties":false},"AgentSTT":{"type":"object","properties":{"language":{"type":["string","null"],"minLength":2,"maxLength":8,"description":"ISO-639-1 or -639-3 code the agent's spoken turns are transcribed in. Omit it and the language is detected per turn. A `language` on `user_audio.start` overrides it.","examples":["en","fa","de"]},"mark_spoken":{"type":["boolean","null"],"description":"Whether a spoken turn reaches the model with a note saying it was transcribed from speech and may contain recognition errors, so the agent reads it for meaning rather than answering a misheard word or pointing out a typo nobody made. Typed turns are never marked, and the note is not stored in the transcript. Unset or null means on."},"model":{"type":["string","null"],"enum":["scribe_v2",null],"description":"Transcription model for this agent's spoken turns. Unset or null means the platform's default model, and follows it if that default ever changes."},"sample_rate":{"type":["integer","null"],"enum":[8000,16000,22050,24000,44100,null],"description":"Hz of the PCM a client sends on `/v1/agent/stream`. One of 8000, 16000, 22050, 24000, 44100.\n\n**Only the socket uses this.** Every other way of sending audio sends a file, and a file states its own rate.\n\nUnset means 24000, which is what the socket *sends* — so a client that records what it plays needs nothing here. 16000 is a third less upstream for audio the transcriber resamples to 16000 anyway, which is the reason to change it. Announced back on `ready` either way.","examples":[16000,24000]}},"additionalProperties":false},"AgentVoice":{"type":"object","properties":{"format":{"type":"string","enum":["wav","pcm"],"default":"wav","description":"The audio format this voice is configured with. On the WebSocket a reply is always raw 16-bit PCM, at the rate its `audio.start` names."},"model":{"type":"string","default":"eleven_flash_v2_5","description":"Which model speaks: `eleven_flash_v2_5` (the default) or `eleven_multilingual_v2`, and one the voice lists as `models` in `GET /v1/voices`."},"provider_options":{"anyOf":[{"$ref":"#/components/schemas/ProviderVoiceSettings"},{"type":"null"}],"description":"The provider's voice settings, on top of the ones the voice was registered with. Unset takes theirs."},"sample_rate":{"type":["integer","null"],"enum":[8000,16000,22050,24000,44100,null],"description":"Hz of the returned audio — one of 8000, 16000, 22050, 24000, 44100, each served natively by the provider. Unset means 24000.\n\nThe response's `sample_rate` and the `X-Sample-Rate` header always say what was actually produced."},"voice_id":{"type":"string","default":"default","description":"An id from `GET /v1/voices` — the built-in voice, a platform voice, or one your organisation uploaded."}},"additionalProperties":false},"AgentSettingsInfo":{"type":"object","properties":{"default_compact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What agents are told when summarising, unless they set their own. Clear it and the built-in instruction applies."},"default_emotion_instruction":{"type":["string","null"],"maxLength":4000,"description":"Default mood-reporting instruction for agents in this organisation that do not set their own. **Overriding it means owning the format**: the gateway strips a first line shaped `mood: <label> <intensity>` — with a label it knows, or with an intensity, or alone on its line — so an instruction asking for any other shape leaves the marker in the reply."},"default_fact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What agents are told to remember about a user, unless they set their own. Clear it and the built-in instruction applies."},"default_relation_instruction":{"type":["string","null"],"maxLength":4000,"description":"Default relationship instruction for agents in this organisation that do not set their own."},"default_stream_chunk_schedule":{"type":["array","null"],"items":{"type":"integer","minimum":1,"maximum":2000},"minItems":1,"maxItems":8,"description":"Default speech chunk schedule for agents in this organisation that do not set their own — see `stream_chunk_schedule` on the agent for what the numbers mean. Ascending. Clear it and the platform's applies."},"resume_greeting_after_minutes":{"type":["integer","null"],"minimum":0,"maximum":43200,"description":"How quiet a conversation must be before the agent greets the person again, in minutes.\n\nA conversation continued inside this window gets no fresh opening line, because re-greeting somebody who refreshed the page thirty seconds ago is worse than silence — and costs a model call and a speech call to be worse. Continued outside it, the agent says hello again, which is what a `first_message` direction reading \"say hello and catch up with them\" is asking for.\n\n**Zero means always greet**, on every connection. Clear it (`null`) and the platform's `agents.greeting.resume_after_minutes` applies (`GET /v1/platform/limits`).\n\nOrganisation-wide: every agent here uses it. It is read when a socket connects, so a change reaches the next connection rather than the ones already open."},"resume_max_age_days":{"type":["integer","null"],"minimum":0,"maximum":365,"description":"How old this person's last conversation may be and still be **continued** rather than replaced, in days.\n\nApplies to `POST /v1/agent/sessions` when it resumes — see `resume` on that request. Past this, opening a session starts a fresh conversation instead. Nothing is lost at the boundary: durable facts and the standing relationship belong to the person rather than the conversation, and carry into every new one.\n\n**Zero means never resume** — every session is a new conversation, which is what an integration that treats a visit as a conversation wants. Clear it (`null`) and the platform's `agents.sessions.resume_max_age_days` applies (`GET /v1/platform/limits`)."},"built_in_compact_instruction":{"type":"string","description":"The summarising instruction used when neither the agent nor the organisation sets one."},"built_in_emotion_instruction":{"type":"string","description":"The platform's mood-reporting instruction, served as it stands so the console never shows a stale copy of it."},"built_in_fact_instruction":{"type":"string","description":"The extraction instruction used when neither the agent nor the organisation sets one."},"built_in_relation_instruction":{"type":"string","description":"The platform's relationship instruction, served as it stands so the console never shows a stale copy of it."},"moods":{"type":"array","items":{"type":"string"},"description":"Every mood label the gateway accepts. A reply reporting anything else is stored with no mood rather than with a made-up one, so an overridden instruction should name these."}},"required":["default_compact_instruction","default_emotion_instruction","default_fact_instruction","default_relation_instruction","default_stream_chunk_schedule","resume_greeting_after_minutes","resume_max_age_days","built_in_compact_instruction","built_in_emotion_instruction","built_in_fact_instruction","built_in_relation_instruction","moods"]},"AgentSettingsUpdate":{"type":"object","properties":{"default_compact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What agents are told when summarising, unless they set their own. Clear it and the built-in instruction applies."},"default_emotion_instruction":{"type":["string","null"],"maxLength":4000,"description":"Default mood-reporting instruction for agents in this organisation that do not set their own. **Overriding it means owning the format**: the gateway strips a first line shaped `mood: <label> <intensity>` — with a label it knows, or with an intensity, or alone on its line — so an instruction asking for any other shape leaves the marker in the reply."},"default_fact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What agents are told to remember about a user, unless they set their own. Clear it and the built-in instruction applies."},"default_relation_instruction":{"type":["string","null"],"maxLength":4000,"description":"Default relationship instruction for agents in this organisation that do not set their own."},"default_stream_chunk_schedule":{"type":["array","null"],"items":{"type":"integer","minimum":1,"maximum":2000},"minItems":1,"maxItems":8,"description":"Default speech chunk schedule for agents in this organisation that do not set their own — see `stream_chunk_schedule` on the agent for what the numbers mean. Ascending. Clear it and the platform's applies."},"resume_greeting_after_minutes":{"type":["integer","null"],"minimum":0,"maximum":43200,"description":"How quiet a conversation must be before the agent greets the person again, in minutes.\n\nA conversation continued inside this window gets no fresh opening line, because re-greeting somebody who refreshed the page thirty seconds ago is worse than silence — and costs a model call and a speech call to be worse. Continued outside it, the agent says hello again, which is what a `first_message` direction reading \"say hello and catch up with them\" is asking for.\n\n**Zero means always greet**, on every connection. Clear it (`null`) and the platform's `agents.greeting.resume_after_minutes` applies (`GET /v1/platform/limits`).\n\nOrganisation-wide: every agent here uses it. It is read when a socket connects, so a change reaches the next connection rather than the ones already open."},"resume_max_age_days":{"type":["integer","null"],"minimum":0,"maximum":365,"description":"How old this person's last conversation may be and still be **continued** rather than replaced, in days.\n\nApplies to `POST /v1/agent/sessions` when it resumes — see `resume` on that request. Past this, opening a session starts a fresh conversation instead. Nothing is lost at the boundary: durable facts and the standing relationship belong to the person rather than the conversation, and carry into every new one.\n\n**Zero means never resume** — every session is a new conversation, which is what an integration that treats a visit as a conversation wants. Clear it (`null`) and the platform's `agents.sessions.resume_max_age_days` applies (`GET /v1/platform/limits`)."}},"additionalProperties":false},"AgentUpdate":{"type":"object","properties":{"code_execution":{"type":["boolean","null"],"description":"Whether the model may write and run Python while it replies — sums, unit conversions, dates — and answer from what it printed. Gemini's own tool: it runs at Google, with no network, and nothing of yours.\n\n**No fee, but not free**: the code and its output are model tokens, billed like any reply."},"compact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What to tell the model when summarising. Unset uses the organisation's default, then a built-in instruction written for in-character conversations."},"compact_max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Length cap on the summary, in tokens — the model's thinking included, which shares the budget. Unset uses the platform's `agents.memory.compaction.max_output_tokens` on the Limits page, twice it when the compaction extracts facts too."},"compact_model":{"type":["string","null"],"maxLength":64,"description":"Model for the summarising call. Unset uses the agent's own."},"compact_thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"Thinking level for the summarising call."},"compact_threshold_tokens":{"type":["integer","null"],"minimum":1,"maximum":200000,"description":"Compact automatically once a reply's first model call reads this many tokens — the conversation, its summary and the instruction, without the inline knowledge or the turn's looked-up passages, which no summary shrinks. Tool results do not count. Unset uses the platform's default, `agents.memory.compaction.default_threshold_tokens` on the Limits page. At most 200,000, and at least twice what the instruction costs on its own. `POST /v1/agent/sessions/{id}/compact` works whatever it is."},"emotion_enabled":{"type":["boolean","null"],"description":"Whether the model reports its own mood — one line in front of each reply, which is taken off the reply and sent as the socket's `mood` event, and kept on each stored message.\n\nCosts about six output tokens per turn and no extra model call. The reply text you are handed does not contain the line: a marker with a known label, or with an intensity, or alone on its own line is always removed, and only a marker with an unknown label, no intensity and prose on the same line is left — at that point it cannot be told from a sentence that happens to start with the word."},"emotion_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about reporting a mood. Unset uses the organisation's default, then a built-in one naming every supported label.\n\n**Override this and you own the format.** The gateway strips a first line shaped `mood: <label> <intensity>` — with a label it knows, or with an intensity, or alone on its line — and records a mood only for a label it knows. An instruction that asks for any other shape leaves the model's line in the reply as text and stores no mood."},"environment":{"type":["string","null"],"maxLength":8000,"description":"The setting the conversation takes place in."},"extract_facts_on_compact":{"type":["boolean","null"],"description":"Whether a compaction also extracts durable facts about the user, in the same model call. A caller can always override this per request; this is what the automatic path uses."},"fact_instruction":{"type":["string","null"],"maxLength":4000,"description":"What counts as worth remembering. Unset falls back to the organisation's default, then to a built-in instruction."},"facts_enabled":{"type":["boolean","null"],"description":"Whether this agent remembers facts about the people it talks to. **On by default**, unlike most switches here.\n\nOff, its conversations are never read for facts and its turns recall none — nothing is charged for either — and a compaction extracts nothing whatever it is asked. Facts already stored stay, and other agents still recall the ones they share. Turning it back on reads only what is said from then on."},"facts_scope":{"type":["string","null"],"enum":["person","agent",null],"description":"Whose facts this agent sees and adds to. `person` (the default, and what unset means): what any of your agents learnt about the person that shares. `agent`: its own only — what it learns is not recalled by your other agents, and it does not recall theirs.\n\nA change applies to what it learns from then on: a fact keeps the scope it was learnt under.","examples":["person"]},"first_message":{"type":["string","null"],"maxLength":4000,"description":"The agent's opening line — or, with `first_message_generated` on, the direction for one.\n\nAs a line it costs no tokens and is identical every time. As a direction (\"say hello and catch up with them\") the model writes it fresh, so it can use whatever long-term memory recalled about that person — at the cost of one billed model call.\n\n**Either way it is delivered as the agent's first turn on `/v1/agent/stream`**, so it is streamed and spoken a sentence at a time like every other reply. Opening a session with `POST /v1/agent/sessions` does not produce it, and an integration that never opens a socket never hears it.\n\nThe agent is not greeted twice: a conversation continued within a few minutes gets no fresh opening line, and `ready.greeting` says which before a frame arrives."},"first_message_generated":{"type":["boolean","null"],"description":"Whether `first_message` is a direction for the model rather than the words themselves.\n\n**The same text means different things under this flag**, and nothing on the server can tell them apart. Turning it off while the text is still written as an instruction shows that instruction to your users verbatim.\n\nIt needs a `first_message` to act on: switched on with the text empty is refused (422), because an agent with a direction to follow and no direction never greets."},"goal":{"type":["string","null"],"maxLength":8000,"description":"What it is trying to achieve in the conversation."},"guardrails":{"type":["string","null"],"maxLength":8000,"description":"What it must not do or discuss."},"knowledge_enabled":{"type":["boolean","null"],"description":"Whether the agent may use the knowledge entries assigned to it. **Defaults to on**, unlike the other switches here: knowledge works as soon as an entry is assigned, and this only takes it away again.\n\nOff, the entries are not listed in the system instruction and the `search_knowledge` tool is not declared, so the agent answers as though it had none. **Nothing is deleted or unassigned** — turning it back on restores exactly what was there, which is what makes this the way to try an agent without its reference material.\n\nAssign entries with `PUT /v1/agents/{agent_id}/knowledge`."},"knowledge_lookup_first":{"type":["boolean","null"],"description":"When knowledge is looked up: search before every reply and hand the model what matches, so it never answers from memory what its material says. **On by default.** Off, the model searches only when it decides to, which is slower when it does and wrong more often when it does not."},"knowledge_max_results":{"type":["integer","null"],"minimum":1,"maximum":8,"description":"Pieces of material a lookup hands the model, 1-8. Unset is the platform's `agents.knowledge.default_results` (`GET /v1/platform/limits`)."},"knowledge_mode":{"type":["string","null"],"enum":["auto","prompt","lookup",null],"description":"How the agent reads its knowledge. `auto` (the default, and what unset means) puts the whole text in the prompt while it fits the platform's `agents.knowledge.inline_budget_tokens` — no lookup, nothing to miss — and looks it up beyond that. `prompt` puts it in the prompt up to the platform's `agents.knowledge.prompt_cap_tokens` (each turn's first word is slower the more there is). `lookup` always looks it up. Both are on `GET /v1/platform/limits`.","examples":["auto"]},"max_facts":{"type":["integer","null"],"minimum":1,"maximum":100,"description":"The most of the person's facts put in front of the model, 1-100. Unset is as many as fit in the platform's `agents.memory.recall.budget_chars` (`GET /v1/platform/limits`; 6,000 characters is some 1,500 tokens). When they do not all fit, the ones a conversation confirmed or learnt most recently go in. They are prompt tokens on every turn."},"max_output_tokens":{"type":["integer","null"],"minimum":1,"maximum":65536,"description":"Cap on reply length. Unset means the model's own limit."},"model":{"type":["string","null"],"maxLength":64,"description":"Gemini model id. `null` or empty returns it to the default, the platform's `agents.model` (`GET /v1/platform/limits`).","examples":["gemini-3.1-flash-lite"]},"name":{"type":"string","minLength":1,"maxLength":255,"description":"Human-readable name. Shown in the console; never sent to the model."},"personality":{"type":["string","null"],"maxLength":8000,"description":"Who the agent is — character, background, quirks."},"relation_enabled":{"type":["boolean","null"],"description":"Whether the agent keeps a standing relationship with each person it talks to — warmth, trust and how well they know each other, carried across every conversation with that end user and never forgotten.\n\nTurning this on declares a tool the model calls when something changes its view of them. **It adds no latency to an ordinary turn**: the provider returns the reply and the tool call together, and the call is answered on the next turn. The one exception is a reply that is only the call with no words in it, which costs a second model call to write them.\n\nRead it with `GET /v1/agent/relations`."},"relation_instruction":{"type":["string","null"],"maxLength":4000,"description":"What the model is told about the relationship it keeps. Unset uses the organisation's default, then a built-in one.\n\nThis is the *disposition* — that the history is real and that most replies should record nothing. The tool's own description covers the mechanism, and is not configurable."},"stream_chunk_schedule":{"type":["array","null"],"items":{"type":"integer","minimum":1,"maximum":2000},"minItems":1,"maxItems":8,"description":"How a streamed reply is cut into pieces for speech, smallest first - for example `[60, 160, 250]`. Ascending; a schedule out of order is refused.\n\n**Only `/v1/agent/stream` uses this**; a buffered turn has the whole reply before it speaks and nothing to schedule.\n\nThe **first** number is the one that matters: it decides how much text must arrive before the agent starts talking, so lowering it starts speech sooner. There is a floor worth knowing - a chunk has to be generated faster than the previous one plays, so a first chunk under roughly 50 characters starts sooner and then runs out of audio before the next piece exists, which is heard as a stutter rather than as a fast reply. Later numbers trade first-audio latency for prosody: a longer piece is spoken with better intonation because the model can see more of the sentence at once.\n\nOmit it to use your organisation's default, and then the platform's."},"stt_config":{"anyOf":[{"$ref":"#/components/schemas/AgentSTT"},{"type":"null"}],"description":"How a spoken turn is transcribed — the model, the language hint, and the sample rate a client sends PCM at on `/v1/agent/stream`. Only what you set is stored: a model left unset follows the platform's default.\n\n**Defaults, not policy.** `user_audio.start` may name any of these per turn and what it names wins. There is no switch for hearing, and no scope of its own: a credential that may talk to an agent may speak to it, exactly as it has always been able to hear it speak back."},"thinking_level":{"type":["string","null"],"enum":["minimal","low","medium","high",null],"description":"How hard the model thinks before answering. `minimal` is fastest to first token; `medium` is the model's own default. Unset means the model decides."},"thinking_summaries":{"type":["string","null"],"enum":["auto","none",null],"description":"Whether a readable summary of the reasoning is returned."},"tone":{"type":["string","null"],"maxLength":8000,"description":"How it speaks: register, pacing, verbosity."},"url_context":{"type":["boolean","null"],"description":"Whether the model may read a web page by its address while it replies, when the person gives one or one is in its instructions. Gemini's own tool: the page is fetched by Google. The pages it read come back as the reply's `sources`.\n\n**No fee, but not free**: a page's text is model tokens, billed like any reply — a long page can be thousands. A page is untrusted input: what it says reaches the model, so the most it can steer the agent towards is the agent's own tools, and a tool marked `ask_first` still needs the person."},"voice_config":{"anyOf":[{"$ref":"#/components/schemas/AgentVoice"},{"type":"null"}],"description":"Voice and model settings.\n\n**A speaking agent speaks on `eleven_flash_v2_5`** (used when `model` is left unset) **or `eleven_multilingual_v2`**, with any voice from `GET /v1/voices` that offers it. `eleven_v3` is refused with 422 when `voice_enabled` is true: it cannot stream over the socket agents use. An agent that does not speak may leave this unset."},"voice_enabled":{"type":["boolean","null"],"description":"Whether replies come back as audio as well as text.\n\nSpeech is generated after the reply and adds its own latency — roughly a fifth of the spoken duration. A caller can override this per turn with `speech` on the socket's `user_message` or `user_audio.start`."},"expected_version":{"type":["integer","null"],"description":"The `version` you last read for this agent. When set and the agent has been changed since, the update is refused with 409 and nothing is written - re-read it and decide again. Omit it to write unconditionally."}},"additionalProperties":false},"EndUserInfo":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"display_name":{"type":["string","null"],"description":"The name you gave them, or null."},"external_id":{"type":["string","null"],"description":"Your own id for this person, if you gave one. Null otherwise — every `generated` end user has none.","examples":["user_8842"]},"facts_updated_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When something was last learned or forgotten about this person. Null means nothing has ever been stored about them."},"id":{"type":"string","description":"The end user's identifier. Send it as `end_user_id` when opening a session, and keep it — it is how this person is recognised again, and what their long-term memory is filed under.","examples":["eu_7Kq2mZp1XvA9dRt4"]},"origin":{"type":"string","enum":["created","generated"],"description":"`created` when you made this end user deliberately, `generated` when a session was opened without an id and one was minted for it.","examples":["created"]},"sessions":{"type":"integer","description":"How many conversations this person has had."}},"required":["created_at","display_name","external_id","facts_updated_at","id","origin","sessions"]},"EndUserCreateRequest":{"type":"object","properties":{"display_name":{"type":["string","null"],"maxLength":128,"description":"A human name, for your dashboard and for reading a transcript. Nothing resolves by it — names are not unique — and it is never shown to the agent unless you put it in the conversation yourself.\n\nOptional, and worth leaving empty if you would rather not store a name here: `eu_...` is not personal data and a name is.","examples":["Sarah Chen"]},"external_id":{"type":["string","null"],"maxLength":128,"description":"Your **own** id for this person — whatever you already call them. Optional, and unique within your organisation.\n\n**Sending it makes this call idempotent**: the same value always returns the same end user, creating one only the first time. That means you never have to store the id we mint — pass yours at every sign-in and the person is recognised, with their whole memory and every relationship intact.\n\nIt can be used anywhere an `end_user_id` is accepted, interchangeably with the `eu_...` form.","examples":["user_8842"]}},"additionalProperties":false},"EndUserPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EndUserInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"EndUserUpdateRequest":{"type":"object","properties":{"display_name":{"type":["string","null"],"maxLength":128,"description":"A human name, for your dashboard and for reading a transcript. Nothing resolves by it — names are not unique — and it is never shown to the agent unless you put it in the conversation yourself.\n\nOptional, and worth leaving empty if you would rather not store a name here: `eu_...` is not personal data and a name is."},"external_id":{"type":["string","null"],"maxLength":128,"description":"Your **own** id for this person — whatever you already call them. Optional, and unique within your organisation.\n\n**Sending it makes this call idempotent**: the same value always returns the same end user, creating one only the first time. That means you never have to store the id we mint — pass yours at every sign-in and the person is recognised, with their whole memory and every relationship intact.\n\nIt can be used anywhere an `end_user_id` is accepted, interchangeably with the `eu_...` form."}},"additionalProperties":false},"EndUserDeleted":{"type":"object","properties":{"id":{"type":"string","description":"The end user that was deleted."},"facts_deleted":{"type":"integer","description":"Durable facts erased with this person — all of them."},"sessions_deleted":{"type":"integer","description":"Conversations this person had, all of them removed."}},"required":["id","facts_deleted","sessions_deleted"]},"KnowledgeCategoryPage":{"type":"object","properties":{"data":{"type":"array","items":{"type":"string"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"KnowledgeInsights":{"type":"object","properties":{"days":{"type":"integer"},"entries":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeEntryUsage"}},"unanswered":{"type":"array","items":{"$ref":"#/components/schemas/UnansweredQuestion"},"description":"The newest fifty, newest first."}},"required":["days","entries","unanswered"]},"KnowledgeEntryUsage":{"type":"object","properties":{"entry_id":{"type":"string"},"replies":{"type":"integer","description":"Replies that drew on this entry in the window."}},"required":["entry_id","replies"]},"UnansweredQuestion":{"type":"object","properties":{"agent_id":{"type":"string"},"agent_name":{"type":"string"},"asked":{"type":"string","description":"What the person said."},"asked_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"outcome":{"type":"string","enum":["none","failed"],"description":"`none` — nothing relevant enough; `failed` — the lookup could not be done.","examples":["none"]},"query":{"type":"string","description":"What was searched for."},"session_id":{"type":"string"},"end_user_id":{"type":["string","null"],"description":"Who asked, by `eu_...` id — the conversation is theirs, read per end user. Null once they are deleted."}},"required":["agent_id","agent_name","asked","asked_at","outcome","query","session_id","end_user_id"]},"KnowledgeSearchResult":{"type":"object","properties":{"embed_ms":{"type":["integer","null"],"description":"How long the question's embedding took; null unasked."},"hits":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSearchHit"}},"mode":{"type":["string","null"],"enum":["lookup","prompt",null],"description":"When `agent_id` was given: `prompt` if that agent has its whole knowledge in its prompt and looks nothing up — the results show what a lookup would find all the same — or `lookup`. Null without one."},"outcome":{"type":"string","enum":["found","none","failed"],"description":"`found`, `none` (nothing relevant enough), or `failed`.","examples":["found"]},"query_ms":{"type":["integer","null"],"description":"How long the index search took; null unasked."}},"required":["embed_ms","hits","mode","outcome","query_ms"]},"KnowledgeSearchHit":{"type":"object","properties":{"entry_id":{"type":"string"},"score":{"type":"number","description":"Cosine similarity, 0-1. Nothing under the platform's `agents.knowledge.min_score` (`GET /v1/platform/limits`) is handed over."},"text":{"type":"string","description":"The piece the agent would read."},"title":{"type":"string"}},"required":["entry_id","score","text","title"]},"KnowledgeSearchRequest":{"type":"object","properties":{"agent_id":{"type":["string","null"],"description":"Search what this agent can find. Omitted, every entry of the organisation that can be searched."},"max_results":{"type":["integer","null"],"minimum":1,"maximum":8},"query":{"type":"string","minLength":1,"maxLength":500,"description":"A question, as a person would ask it.","examples":["Can I get a refund after 20 days?"]}},"required":["query"],"additionalProperties":false},"KnowledgePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"KnowledgeInfo":{"type":"object","properties":{"agents":{"type":"array","items":{"$ref":"#/components/schemas/AssignedAgent"},"description":"Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault."},"body":{"type":"string"},"body_chars":{"type":"integer","description":"How long `body` is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it."},"category":{"type":["string","null"]},"chunks":{"type":"integer","description":"How many pieces the body was split into for searching. Most hand-written entries are one."},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"error":{"type":["string","null"],"description":"Why indexing failed, when it did."},"id":{"type":"string"},"refresh":{"type":["string","null"],"enum":["off","daily","weekly",null],"description":"For an entry made from a web page: how often it is read again. Null for the rest."},"refreshed_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When the page or file was last read."},"searchable":{"type":"boolean","description":"Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable."},"source_kind":{"type":"string","enum":["text","file","url"],"description":"Where the text came from: `text` (written or pasted), `file` or `url`.","examples":["text"]},"source_name":{"type":["string","null"],"description":"The file's name, or the page's address."},"status":{"type":"string","enum":["pending","ready","failed","importing"],"description":"How the **latest** body went: `importing` while a file or a page is read into text, `pending` while it is indexed, `ready` once it is done, `failed` with the reason in `error`. Indexing takes seconds to a few minutes: an entry is `ready` once the search index returns it. Whether an agent can find the entry is `searchable`: an edited entry stays searchable at its previous version while the new one indexes.","examples":["ready"]},"summary":{"type":["string","null"],"description":"One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. **This is what the agent sees on every turn** — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry."},"summary_source":{"type":["string","null"],"enum":["model","customer",null],"description":"Who wrote `summary`: `model` (rewritten with every new body) or `customer` (kept through every edit). Null while there is none.","examples":["model"]},"title":{"type":"string"},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"version":{"type":"integer","description":"Moves with every edit. Send it back as `expected_version` to refuse an edit made over somebody else's."}},"required":["agents","body","body_chars","category","chunks","created_at","error","id","refresh","refreshed_at","searchable","source_kind","source_name","status","summary","summary_source","title","updated_at","version"]},"AssignedAgent":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"KnowledgeCreate":{"type":"object","properties":{"agent_ids":{"type":"array","items":{"type":"string"},"maxItems":200},"body":{"type":"string","minLength":1,"maxLength":100000},"category":{"type":["string","null"],"maxLength":120},"summary":{"type":["string","null"],"maxLength":200},"title":{"type":"string","minLength":1,"maxLength":255}},"required":["body","title"],"additionalProperties":false},"KnowledgeBulkResult":{"type":"object","properties":{"changed":{"type":"integer","description":"Links added or removed, or entries deleted — what the action changed."},"entries":{"type":"integer","description":"Entries the action was applied to."},"skipped":{"type":"array","items":{"type":"string"},"description":"Ids sent that are not this organisation's, ignored."}},"required":["changed","entries","skipped"]},"KnowledgeBulk":{"type":"object","properties":{"action":{"type":"string","enum":["delete","assign","unassign"]},"agent_ids":{"type":"array","items":{"type":"string"},"maxItems":100,"description":"For `assign` and `unassign`: the agents, up to 100."},"entry_ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":200,"description":"Up to 200 entries. Ids not yours come back in `skipped`."}},"required":["action","entry_ids"],"additionalProperties":false},"KnowledgeUrlCreate":{"type":"object","properties":{"agent_ids":{"type":"array","items":{"type":"string"},"maxItems":200},"category":{"type":["string","null"],"maxLength":120},"crawl":{"type":"boolean","default":false,"description":"Also import the pages this one links to and the site's sitemap lists, in the same part of the site — `https://shop.test/help/` finds `/help/…` and nothing else — each as its own entry with the same agents, category and `refresh`. One level, robots.txt honoured, pages already imported skipped. The response is this page's entry; the others appear as they are found."},"max_pages":{"type":"integer","minimum":2,"maximum":100,"default":25,"description":"With `crawl`: the most entries made, this page included."},"refresh":{"type":["string","null"],"enum":["off","daily","weekly",null],"description":"Read the page again on this schedule; when it has changed, the entry is re-indexed. `off` by default."},"title":{"type":["string","null"],"maxLength":255},"url":{"type":"string","minLength":1,"maxLength":2048,"description":"A public http(s) address: a web page, or a PDF, Word, CSV or text file online.","examples":["https://example.com/refunds"]}},"required":["url"],"additionalProperties":false},"KnowledgeUploadTicket":{"type":"object","properties":{"expires_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When `url` stops accepting the upload."},"upload_id":{"type":"string"},"url":{"type":"string","description":"`PUT` the file's bytes here, with `content_type` as the `Content-Type` and no authentication headers, before `expires_at`."},"content_type":{"type":"string","description":"The `Content-Type` the `PUT` must carry: it is signed into the URL, and R2 refuses any other.","examples":["application/pdf"]}},"required":["expires_at","upload_id","url","content_type"]},"KnowledgeUploadRequest":{"type":"object","properties":{"filename":{"type":"string","minLength":1,"maxLength":255,"description":"The file's name; its extension says how it is read: .pdf, .docx, .xlsx, .xls, .ods, .odt, .csv, .html, .htm, .txt, .md.","examples":["LX-900 manual.pdf"]},"size_bytes":{"type":["integer","null"],"minimum":0,"description":"The file's size, when known. At most 10 MB."},"content_type":{"type":["string","null"],"maxLength":255,"description":"The `Content-Type` the `PUT` will carry, signed into the URL. Unset: the type of the file's extension, which the ticket names.","examples":["application/pdf"]}},"required":["filename"],"additionalProperties":false},"KnowledgeFileCreate":{"type":"object","properties":{"agent_ids":{"type":"array","items":{"type":"string"},"maxItems":200},"category":{"type":["string","null"],"maxLength":120},"filename":{"type":"string","minLength":1,"maxLength":255},"title":{"type":["string","null"],"maxLength":255},"upload_id":{"type":"string"}},"required":["filename","upload_id"],"additionalProperties":false},"KnowledgeUpdate":{"type":"object","properties":{"agent_ids":{"type":"array","items":{"type":"string"},"maxItems":1000,"description":"The agents that have this entry, as a complete set — set in the same transaction as the rest of the edit, so a save cannot land half. Omitted, the assignments are left alone."},"body":{"type":"string","minLength":1,"maxLength":1000000},"category":{"type":["string","null"],"maxLength":120},"expected_version":{"type":"integer","description":"The `version` the editor started from. If the entry has been edited since, the edit is refused with 409 rather than writing over somebody else's."},"refresh":{"type":"string","enum":["off","daily","weekly"],"description":"For an entry made from a web page: how often it is read again."},"summary":{"type":["string","null"],"maxLength":200},"title":{"type":"string","minLength":1,"maxLength":255}},"additionalProperties":false},"KnowledgeAssigned":{"type":"object","properties":{"added":{"type":"integer"},"removed":{"type":"integer"},"skipped":{"type":"array","items":{"type":"string"},"description":"Ids sent that are not this organisation's, ignored."}},"required":["added","removed","skipped"]},"KnowledgeAgentsUpdate":{"type":"object","properties":{"agent_ids":{"type":"array","items":{"type":"string"},"maxItems":1000,"description":"The complete set. An empty list clears every assignment, which is a real thing to want and not an error."}},"required":["agent_ids"],"additionalProperties":false},"AgentKnowledgeUpdate":{"type":"object","properties":{"entry_ids":{"type":"array","items":{"type":"string"},"maxItems":1000,"description":"The complete set. An empty list clears every assignment, which is a real thing to want and not an error."}},"required":["entry_ids"],"additionalProperties":false},"ToolPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ToolInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"ToolInfo":{"type":"object","properties":{"id":{"type":"string","description":"The tool's id, `tl_…`.","examples":["tl_4fHq9Zk2LmP0aXbC"]},"name":{"type":"string","description":"What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; `search_knowledge` and `remember_relation` are the platform's own.","examples":["lookup_order"]},"description":{"type":"string"},"kind":{"type":"string","enum":["webhook","app"],"description":"`webhook`: your server, sent a signed `POST` with the call. `app`: the person's app, called over the agent's WebSocket. Fixed at creation."},"parameters":{"type":"object","additionalProperties":{},"description":"Its inputs, as a JSON Schema object: `{\"type\": \"object\", \"properties\": {…}, \"required\": […]}`, each property `string` (with an optional `enum`), `number`, `integer`, `boolean`, `array` (with `items`) or `object`, each with an optional `description`. Nothing else is taken: a key outside this set is refused rather than passed on unenforced.","examples":[{"type":"object","properties":{"order_number":{"type":"string","description":"As printed on the receipt."}},"required":["order_number"]}]},"url":{"type":["string","null"],"description":"A webhook's address; `null` for an app's."},"timeout_ms":{"type":"integer","minimum":1000,"maximum":30000,"description":"How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so."},"ask_first":{"type":"boolean","description":"Never run on the model's word alone: the agent asks the person first, and the tool runs only after they say yes."},"action_only":{"type":"boolean","description":"An app's tool only: the app is told to act and the reply carries on at once, with no answer awaited."},"filler":{"type":["string","null"],"description":"The line said before the call; `null` for none."},"header_names":{"type":"array","items":{"type":"string"},"description":"The names of the headers sent with each call; their values are never shown."},"secret_hint":{"type":["string","null"],"description":"The signing secret's last four characters; `null` for an app's tool."},"agent_ids":{"type":"array","items":{"type":"string"},"description":"The agents it is turned on for."},"version":{"type":"integer","description":"Moves by one on every save. Send it back as `expected_version` on an update to have the save refused (409) if somebody else changed the tool since you read it."},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"recent_failures":{"type":"integer","description":"Its failed calls in your agents' conversations within the platform's `agents.tools.failure_window_minutes` — a deadline missed, an error answered, a server out of reach — counted from the first; 0 once the window passes with none. Each conversation reports its failures at most once in the platform's `agents.tools.report_every_minutes`, so the count can lag by that much. At the platform's `agents.tools.failures_to_mail` in the window, your organisation's admins are mailed, at most once in its `agents.tools.mail_every_hours` per tool. Each is on `GET /v1/platform/limits`."},"last_failed_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When a call last failed; `null` if none has."},"last_error":{"type":["string","null"],"description":"What the model was told of the last failure; `null` if none has."}},"required":["id","name","description","kind","parameters","url","timeout_ms","ask_first","action_only","filler","header_names","secret_hint","agent_ids","version","created_at","updated_at","recent_failures","last_failed_at","last_error"]},"ToolWithSecret":{"allOf":[{"$ref":"#/components/schemas/ToolInfo"},{"type":"object","properties":{"secret":{"type":["string","null"],"description":"The signing secret, `whsec_…`, **shown this once**: every call is signed with it (`X-Tool-Signature`). Keep it on your server. `null` for an app's tool."}},"required":["secret"]}]},"ToolCreate":{"type":"object","properties":{"name":{"type":"string","description":"What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; `search_knowledge` and `remember_relation` are the platform's own.","examples":["lookup_order"]},"description":{"type":"string","minLength":1,"maxLength":1000,"description":"What the tool does and when to call it, in the model's terms: it reads this to decide."},"kind":{"type":"string","enum":["webhook","app"],"description":"`webhook`: your server, sent a signed `POST` with the call. `app`: the person's app, called over the agent's WebSocket. Fixed at creation."},"parameters":{"type":"object","additionalProperties":{},"description":"Its inputs (as above); omitted: it takes none.","examples":[{"type":"object","properties":{"order_number":{"type":"string","description":"As printed on the receipt."}},"required":["order_number"]}]},"url":{"type":["string","null"],"maxLength":2048,"description":"A webhook's `https://` address, public, and not this platform's. An app's tool has none."},"timeout_ms":{"type":"integer","minimum":1000,"maximum":30000,"description":"How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so."},"ask_first":{"type":"boolean","description":"Never run on the model's word alone: the agent asks the person first, and the tool runs only after they say yes."},"action_only":{"type":"boolean","description":"An app's tool only: the app is told to act and the reply carries on at once, with no answer awaited."},"filler":{"type":["string","null"],"maxLength":200,"description":"A line the agent says before the call, while it waits: `Let me check that for you.` `null`: none."},"headers":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"A webhook's own headers, sent with every call (your API token): at most 10, stored encrypted and never shown again — a tool answers their names. `Content-Type`, `User-Agent`, `X-Tool-*` and HTTP's own are the platform's.","examples":[{"Authorization":"Bearer sk_shop_…"}]}},"required":["name","description","kind"],"additionalProperties":false},"ToolCallPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ToolCall"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"ToolCall":{"type":"object","properties":{"at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When the call ended."},"call_id":{"type":"string","description":"The call's id: the `call_id` your server was sent, and the turn's `tool_calls` carry."},"agent_id":{"type":"string"},"session_id":{"type":"string","description":"The conversation it was called in."},"end_user_id":{"type":"string","description":"The person, `eu_…`, whose conversation it was."},"ok":{"type":"boolean","description":"It answered in time, and not with an error."},"duration_ms":{"type":"integer"},"error":{"type":["string","null"],"description":"What the model was told failed; `null` when nothing did."}},"required":["at","call_id","agent_id","session_id","end_user_id","ok","duration_ms","error"]},"ToolUpdate":{"type":"object","properties":{"name":{"type":"string","description":"What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; `search_knowledge` and `remember_relation` are the platform's own.","examples":["lookup_order"]},"description":{"type":"string","minLength":1,"maxLength":1000,"description":"What the tool does and when to call it, in the model's terms: it reads this to decide."},"parameters":{"type":"object","additionalProperties":{},"description":"Its inputs, as a JSON Schema object: `{\"type\": \"object\", \"properties\": {…}, \"required\": […]}`, each property `string` (with an optional `enum`), `number`, `integer`, `boolean`, `array` (with `items`) or `object`, each with an optional `description`. Nothing else is taken: a key outside this set is refused rather than passed on unenforced.","examples":[{"type":"object","properties":{"order_number":{"type":"string","description":"As printed on the receipt."}},"required":["order_number"]}]},"url":{"type":"string","maxLength":2048,"description":"A webhook's `https://` address, public, and not this platform's. An app's tool has none."},"timeout_ms":{"type":"integer","minimum":1000,"maximum":30000,"description":"How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so."},"ask_first":{"type":"boolean","description":"Never run on the model's word alone: the agent asks the person first, and the tool runs only after they say yes."},"action_only":{"type":"boolean","description":"An app's tool only: the app is told to act and the reply carries on at once, with no answer awaited."},"filler":{"type":["string","null"],"maxLength":200,"description":"A line the agent says before the call, while it waits: `Let me check that for you.` `null`: none."},"headers":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"Replaces every header; `null` or `{}` sends none.","examples":[{"Authorization":"Bearer sk_shop_…"}]},"expected_version":{"type":["integer","null"],"description":"The `version` you last read. When set and the tool has been changed since, the update is refused with 409 and nothing is written. Omit it to write unconditionally."}},"additionalProperties":false},"ToolTestResult":{"type":"object","properties":{"ok":{"type":"boolean","description":"Your server answered 2xx within the timeout."},"status":{"type":["integer","null"],"description":"Its HTTP status; `null` when none came (unreachable, or too slow)."},"duration_ms":{"type":"integer","description":"From sending to its answer's last byte."},"result":{"description":"Its answer: the JSON it sent, else its text, cut at the platform's `agents.tools.result_max_bytes` (`GET /v1/platform/limits`); `null` for none."},"truncated":{"type":"boolean","description":"The answer was longer than the platform's `agents.tools.result_max_bytes` and was cut."},"error":{"type":["string","null"],"description":"What failed, as the model would be told; `null` when it did not."}},"required":["ok","status","duration_ms","truncated","error"]},"ToolTestRequest":{"type":"object","properties":{"arguments":{"type":"object","additionalProperties":{},"description":"What the call carries as `arguments`; `{}` when omitted."}},"additionalProperties":false},"ToolsAssigned":{"type":"object","properties":{"added":{"type":"integer"},"removed":{"type":"integer"},"skipped":{"type":"array","items":{"type":"string"},"description":"Ids sent that are not this organisation's, ignored."}},"required":["added","removed","skipped"]},"AgentToolsUpdate":{"type":"object","properties":{"tool_ids":{"type":"array","items":{"type":"string"},"maxItems":200,"description":"The complete set, at most the platform's `limits.agent.tools` (`GET /v1/platform/limits`). An empty list turns every tool off."}},"required":["tool_ids"],"additionalProperties":false},"TTSRequest":{"type":"object","properties":{"voice_id":{"type":"string","default":"default","description":"An id from `GET /v1/voices` — the built-in voice, a platform voice, or one your organisation uploaded."},"model":{"type":"string","default":"eleven_flash_v2_5","description":"Which model speaks: one of the voice's `models` in `GET /v1/voices` — `eleven_flash_v2_5` (the fastest, and the default), `eleven_multilingual_v2` or `eleven_v3`. A model the voice does not list is refused."},"provider_options":{"anyOf":[{"$ref":"#/components/schemas/ProviderVoiceSettings"},{"type":"null"}],"default":null,"description":"The provider's voice settings, on top of the ones the voice was registered with. Unset takes theirs."},"format":{"type":"string","enum":["wav","pcm"],"default":"wav","description":"Output audio format"},"sample_rate":{"type":["integer","null"],"enum":[8000,16000,22050,24000,44100,null],"description":"Hz of the returned audio — one of 8000, 16000, 22050, 24000, 44100, each served natively by the provider. Unset means 24000.\n\n`POST /v1/tts` says what it produced in the `X-Sample-Rate` header. Queued speech (`POST /v1/audio`) does not take it: each of its formats has its own rate."},"text":{"type":"string","minLength":1,"maxLength":2000},"stream":{"type":"boolean","default":false,"description":"Stream audio as it is generated. Only the pcm format can stream."}},"required":["text"],"additionalProperties":false},"Transcription":{"type":"object","properties":{"text":{"type":"string"},"language_code":{"type":["string","null"]},"language_probability":{"type":["number","null"]},"duration_seconds":{"type":"number","description":"The audio's length, by the provider's measure: what is charged"},"model":{"type":"string"},"words":{"type":["array","null"],"items":{"$ref":"#/components/schemas/TranscriptWord"},"description":"Each word and its timing, with `timestamps`; `null` otherwise"}},"required":["text","language_code","language_probability","duration_seconds","model","words"]},"TranscriptWord":{"type":"object","properties":{"text":{"type":"string"},"start_seconds":{"type":["number","null"],"description":"When it starts, in seconds from the audio's start."},"end_seconds":{"type":["number","null"],"description":"When it ends, in seconds from the audio's start."},"type":{"type":["string","null"],"enum":["word","spacing","audio_event",null],"description":"`word`, the `spacing` between words, or an `audio_event` (with `tag_audio_events`); `null` when the provider did not say."},"speaker_id":{"type":["string","null"],"description":"Who said it, with `diarize`; `null` otherwise."}},"required":["text","start_seconds","end_seconds","type","speaker_id"]},"TranscriptionStreamLine":{"oneOf":[{"$ref":"#/components/schemas/StreamSegment"},{"$ref":"#/components/schemas/StreamTranscript"},{"$ref":"#/components/schemas/StreamError"},{"$ref":"#/components/schemas/StreamDone"}],"discriminator":{"propertyName":"type","mapping":{"segment":"#/components/schemas/StreamSegment","transcript":"#/components/schemas/StreamTranscript","error":"#/components/schemas/StreamError","done":"#/components/schemas/StreamDone"}}},"StreamSegment":{"type":"object","properties":{"type":{"type":"string","enum":["segment"]},"index":{"type":"integer"},"duration_seconds":{"type":"number","description":"The segment's length, as sent to be transcribed."},"reason":{"type":"string","enum":["silence","max","end"],"description":"Where it was cut: at a pause (`silence`), at the longest a segment may be (`max`), or at the end of the audio (`end`)."}},"required":["type","index","duration_seconds","reason"]},"StreamTranscript":{"type":"object","properties":{"type":{"type":"string","enum":["transcript"]},"index":{"type":"integer"},"text":{"type":"string"},"duration_seconds":{"type":"number","description":"The segment's length, as sent: what is charged."},"reason":{"type":"string","enum":["silence","max","end"],"description":"Where it was cut: at a pause (`silence`), at the longest a segment may be (`max`), or at the end of the audio (`end`)."},"language_code":{"type":["string","null"]},"language_probability":{"type":["number","null"]},"words":{"type":["array","null"],"items":{"$ref":"#/components/schemas/TranscriptWord"},"description":"Each word and its timing within the segment, with `timestamps`; `null` otherwise"}},"required":["type","index","text","duration_seconds","reason","language_code","language_probability","words"]},"StreamError":{"type":"object","properties":{"type":{"type":"string","enum":["error"]},"index":{"type":"integer"},"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string"}},"required":["type","index","code","message"]},"StreamDone":{"type":"object","properties":{"type":{"type":"string","enum":["done"]},"text":{"type":"string","description":"The whole transcript."},"segments":{"type":"integer","description":"How many segments the audio was cut into."},"failed":{"type":"integer","description":"How many of them failed, each on its own `error` line."},"duration_seconds":{"type":"number","description":"The transcribed segments' length together."},"language_code":{"type":["string","null"]}},"required":["type","text","segments","failed","duration_seconds","language_code"]},"VoiceInfoPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VoiceInfo"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` when this is the last."}},"required":["data","next_cursor"]},"VoiceInfo":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"kind":{"$ref":"#/components/schemas/VoiceKind"},"status":{"$ref":"#/components/schemas/VoiceStatus"},"duration_seconds":{"type":["number","null"],"description":"Length of the recording it was cloned from, in seconds; `null` for a voice not cloned from one."},"provider":{"$ref":"#/components/schemas/SpeechProvider"},"models":{"type":"array","items":{"type":"string"},"description":"Which models this voice may be spoken with. Send one of these as `model`; anything else is refused."},"samples":{"type":"array","items":{"$ref":"#/components/schemas/VoiceSample"},"description":"A spoken sample per model. Always one entry per model, whether or not it has been generated yet. Empty for the built-in voice."},"created_at":{"type":["string","null"],"format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When it was added; `null` for the built-in voice."}},"required":["id","name","description","kind","status","duration_seconds","provider","models","samples","created_at"]},"VoiceKind":{"type":"string","enum":["builtin","platform","org"],"description":"builtin: ships with the platform. platform: added by the platform operator, available to everyone. org: uploaded or designed by your organisation and visible only to it."},"VoiceStatus":{"type":"string","enum":["pending","ready","failed"],"description":"pending: being cloned, and not yet usable for speech. ready: usable. failed: the clone was refused; `error` says why, and a retry runs it again."},"SpeechProvider":{"type":"string","enum":["elevenlabs"],"description":"Who generates the voice: `elevenlabs`, whom we speak to on your behalf, billed at that provider's own rate."},"VoiceSample":{"type":"object","properties":{"model":{"type":"string"},"ready":{"type":"boolean"},"error":{"type":["string","null"],"description":"Why the sample could not be made; `null` otherwise."},"url":{"type":["string","null"],"description":"Temporary link to the audio, or null when it is not ready. Expires — read it from a fresh response rather than storing it."}},"required":["model","ready","error","url"]},"VoiceUploadTicket":{"type":"object","properties":{"upload_id":{"type":"string","description":"The voice's id to be: pass it to the create once the `PUT` is done."},"url":{"type":"string","description":"PUT the recording here with the `Content-Type` below and no authentication headers, before `expires_at`."},"content_type":{"type":"string"},"expires_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"],"description":"When `url` stops accepting the recording."}},"required":["upload_id","url","content_type","expires_at"]},"VoiceUploadRequest":{"type":"object","properties":{"content_type":{"type":"string","description":"The recording's type — `audio/wav`, `audio/mpeg`, `audio/mp4`, `audio/ogg`, `audio/webm`… — signed into the URL: the `PUT` must send exactly this `Content-Type`.","examples":["audio/mpeg"]}},"required":["content_type"],"additionalProperties":false},"VoiceDetail":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"kind":{"$ref":"#/components/schemas/VoiceKind"},"status":{"$ref":"#/components/schemas/VoiceStatus"},"error":{"type":["string","null"],"description":"Why a `failed` voice failed; `null` otherwise."},"source":{"$ref":"#/components/schemas/VoiceSource"},"design_prompt":{"type":["string","null"],"description":"What a designed voice was described as; `null` otherwise."},"duration_seconds":{"type":["number","null"],"description":"Length of the recording it was cloned from, in seconds; `null` for a voice not cloned from one."},"samples":{"type":"array","items":{"$ref":"#/components/schemas/VoiceSample"}},"provider":{"$ref":"#/components/schemas/SpeechProvider"},"external_voice_id":{"type":["string","null"],"description":"The provider's id for the voice; `null` until it is cloned."},"models":{"type":"array","items":{"type":"string"}},"provider_defaults":{"$ref":"#/components/schemas/ProviderVoiceSettingsInfo"},"created_at":{"type":"string","format":"date-time","examples":["2026-10-24T09:30:00.000Z"]},"created_by_clerk_user_id":{"type":["string","null"],"description":"The person who added it, from a screen; `null` otherwise."},"created_by_api_key_id":{"type":["string","null"],"description":"The API key that added it; `null` otherwise."}},"required":["id","name","description","kind","status","error","source","design_prompt","duration_seconds","samples","provider","external_voice_id","models","provider_defaults","created_at","created_by_clerk_user_id","created_by_api_key_id"]},"VoiceSource":{"type":"string","enum":["upload","design"],"description":"upload: cloned from a recording. design: kept from the voice designer."},"VoiceUpload":{"type":"object","properties":{"upload_id":{"type":"string","description":"From the ticket, after the recording was `PUT` there."},"name":{"type":"string","maxLength":120},"description":{"type":"string","maxLength":500,"default":""},"consent":{"type":"boolean","default":false,"description":"`true` confirms you have the right to clone this voice; the upload is refused without it."}},"required":["upload_id","name"],"additionalProperties":false},"VoiceDesignResult":{"type":"object","properties":{"previews":{"type":"array","items":{"$ref":"#/components/schemas/VoiceDesignPreview"}},"description":{"type":"string","description":"The brief the previews were designed from."},"script":{"type":"string","description":"What the previews say."},"improved":{"type":"boolean","description":"Whether the description was rewritten into a brief; `false` when it was used as it came."}},"required":["previews","description","script","improved"]},"VoiceDesignPreview":{"type":"object","properties":{"id":{"type":"string"},"audio_base64":{"type":"string"},"media_type":{"type":"string"},"duration_seconds":{"type":"number"},"long_enough":{"type":"boolean","description":"Whether the preview is long enough to be kept as a voice."}},"required":["id","audio_base64","media_type","duration_seconds","long_enough"]},"VoiceDesignRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"prompt":{"type":"string","minLength":10,"maxLength":1000}},"required":["name","prompt"],"additionalProperties":false},"VoiceDesignSaveRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"preview_id":{"type":"string","minLength":1,"maxLength":64,"description":"The chosen preview's `id`, from the design's answer."},"description":{"type":"string","maxLength":500,"default":""},"prompt":{"type":"string","maxLength":1000,"default":""}},"required":["name","preview_id"],"additionalProperties":false},"VoiceUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":["string","null"],"maxLength":500},"models":{"type":"array","items":{"type":"string"},"minItems":1},"provider_defaults":{"anyOf":[{"$ref":"#/components/schemas/ProviderVoiceSettings"},{"type":"null"}]}},"additionalProperties":false}},"securitySchemes":{"BearerToken":{"type":"http","scheme":"bearer"},"ApiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"}}},"paths":{"/health":{"get":{"operationId":"getHealth","summary":"Liveness check (no auth required)","tags":["system"],"security":[],"responses":{"200":{"description":"The API is up, with its version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/v1/platform/limits":{"get":{"operationId":"getPlatformLimits","tags":["system"],"summary":"The platform's limits (no auth required)","description":"Every limit this platform holds an organisation to that is not a plan's own: how many tools, voices, knowledge entries and uploaded files it may keep, how large an upload may be, how many generations may be unfinished at once, the requests a minute when a plan sets none, and the ceilings on an ephemeral key. The platform's staff may change them, so a description elsewhere in this reference names a limit by its `key` here rather than by a number. One page: `next_cursor` is always null.","security":[],"responses":{"200":{"description":"Every limit, as it stands now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformLimitPage"}}}}}}},"/v1/ephemeral-keys/limits":{"get":{"operationId":"getEphemeralKeyLimits","tags":["ephemeral keys"],"summary":"What a token may be minted with","responses":{"200":{"description":"The ceilings this platform will mint within","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralKeyLimits"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/ephemeral-keys":{"post":{"operationId":"createEphemeralKey","tags":["ephemeral keys"],"summary":"Mint a token for one end user","description":"**A token is prepaid**: its `credit_limit_micros` is set aside from your balance when it is minted — refused with 402 when the balance cannot cover it — and what it does not spend comes back when it expires, runs out or is revoked. Your plan caps a token's limit and lifetime for one end user, how many each end user may hold, how many you mint a minute (429) and how much credit live tokens may hold at once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralKeyCreateRequest"}}}},"responses":{"201":{"description":"The token, returned **once** — only a hash is stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralKeyCreateResponse"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The balance cannot set aside this credit limit, or the organisation is suspended or on no plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Minted with an ephemeral key, or a revoked one","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Scopes other than [\"agent\"], a minting key without `agent`, an unknown end user, or a limit out of range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"The plan's ephemeral keys a minute, or a request window — `Retry-After` says when","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listEphemeralKeys","tags":["ephemeral keys"],"summary":"This organisation's tokens","parameters":[{"schema":{"type":"boolean","default":true},"required":false,"name":"live_only","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Live tokens, unless you asked for the rest too","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralKeyPage"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/ephemeral-keys/{key_id}":{"get":{"operationId":"getEphemeralKey","tags":["ephemeral keys"],"summary":"One token","description":"A token as the organisation's records hold it, live or not. After a revoke, read it until `settled_at` is set: its spend and refund are final from then.","parameters":[{"schema":{"type":"string"},"required":true,"name":"key_id","in":"path"}],"responses":{"200":{"description":"The token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralKeyInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown ephemeral key id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"revokeEphemeralKey","tags":["ephemeral keys"],"summary":"Revoke a token","description":"Answers at once, with the figures from before its end user's records settle it. `GET /v1/ephemeral-keys/{key_id}` reads the final ones once `settled_at` is set — seconds, longer while a memory job it paid for still runs.","parameters":[{"schema":{"type":"string"},"required":true,"name":"key_id","in":"path"}],"responses":{"200":{"description":"The token is dead; anything in flight still settles","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralKeyInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown ephemeral key id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/token":{"get":{"operationId":"getToken","tags":["ephemeral keys"],"summary":"The credential you are calling with","description":"Read it when a session starts and after a response — not on a timer.","responses":{"200":{"description":"What this credential is, what it may do and what it has left","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenSelf"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/usage":{"get":{"operationId":"getUsage","tags":["usage"],"summary":"Usage for your organisation","description":"Totals, a per-capability breakdown, a daily series and a per-key split over a window ending now, plus your plan and balance. `units_seconds` is seconds of audio generated; `credits_micros` is what was charged, and is null for a group nothing has priced yet.\n\nA report — four aggregates over the window. Poll `GET /v1/plan` for the balance alone.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":365,"default":30,"description":"Window ending now"},"required":false,"description":"Window ending now","name":"days","in":"query"}],"responses":{"200":{"description":"The report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/plan":{"get":{"operationId":"getPlan","tags":["usage"],"summary":"Your plan and credit balance","description":"What plan your organisation is on, what it grants each cycle, what is left, and when the cycle ends. Two indexed lookups, so poll it freely — this is what to check before queueing a batch rather than learning from a 402.\n\n`credit_balance_micros` may exceed `monthly_credits_micros` after a manual adjustment, and may be slightly negative inside the grace buffer. Null when the organisation has no subscription.","responses":{"200":{"description":"The plan and balance, or null with no subscription","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/OrgPlan"},{"type":"null"}]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/images":{"post":{"operationId":"createImage","tags":["images"],"summary":"Generate an image","description":"Accepts the request, charges for it and returns immediately with a `gen_…` id. Poll `GET /v1/images/{id}` until `status` is `succeeded`, `failed` or `moderated`.\n\n**Charged when accepted, not when finished.** The price is per megapixel: the output's, with a one-megapixel minimum, plus each reference's at the same rate. A generation that fails or is refused by the content filter is refunded in full.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageRequest"}}}},"responses":{"202":{"description":"Accepted and charged. Poll `GET /v1/images/{id}`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/images/{generation_id}":{"get":{"operationId":"getImage","tags":["images"],"summary":"Check an image","description":"Poll every couple of seconds until `status` is terminal. On success `image.url` is a short-lived link; the image is also in your asset library under `image.asset_id`.","parameters":[{"schema":{"type":"string"},"required":true,"name":"generation_id","in":"path"}],"responses":{"200":{"description":"The generation — the image once it succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/videos":{"post":{"operationId":"createVideo","tags":["videos"],"summary":"Generate a video","description":"Accepts the request, charges for it and returns immediately with a `gen_…` id. Poll `GET /v1/videos/{id}`; a clip takes minutes.\n\n**Charged when accepted**, per second at a rate that depends on `resolution`; a failure is refunded in full. Give `start_frame` (and `end_frame`) to animate images from your library. **There is a limit on how many of your videos may be unfinished at once** — the platform's `limits.org.active_generations.video` (`GET /v1/platform/limits`): over it, this answers 429 without charging anything.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoRequest"}}}},"responses":{"202":{"description":"Accepted and charged. Poll `GET /v1/videos/{id}`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/videos/{generation_id}":{"get":{"operationId":"getVideo","tags":["videos"],"summary":"Check a video","parameters":[{"schema":{"type":"string"},"required":true,"name":"generation_id","in":"path"}],"responses":{"200":{"description":"The generation — the clip once it succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/audio":{"post":{"operationId":"createAudio","tags":["audio"],"summary":"Generate speech","description":"Accepts the request, charges for it and returns immediately with a `gen_…` id. Poll `GET /v1/audio/{id}`.\n\n**Use `/v1/tts` instead if you need the audio in the response.** This route is for what a queue makes possible: mp3 and opus, longer text, and a result in your asset library. Charged per thousand characters when accepted; a failure is refunded.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioRequest"}}}},"responses":{"202":{"description":"Accepted and charged. Poll `GET /v1/audio/{id}`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No voice by that `voice_id`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/audio/{generation_id}":{"get":{"operationId":"getAudio","tags":["audio"],"summary":"Check a speech generation","parameters":[{"schema":{"type":"string"},"required":true,"name":"generation_id","in":"path"}],"responses":{"200":{"description":"The generation — the audio once it succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/skybox":{"post":{"operationId":"createSkybox","tags":["skybox"],"summary":"Generate a 360 environment","description":"Accepts the request, charges for it and returns immediately with a `gen_…` id. Poll `GET /v1/skybox/{id}`.\n\n**One flat price per generation.** **`skybox.url` is a 32-bit Radiance `.hdr`** and will not render in a browser; use `skybox.preview_url` for a 360 viewer. A failure is refunded in full.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkyboxRequest"}}}},"responses":{"202":{"description":"Accepted and charged. Poll `GET /v1/skybox/{id}`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/skybox/styles":{"get":{"operationId":"listSkyboxStyles","tags":["skybox"],"summary":"List skybox styles","description":"The provider's current styles, each with the id to pass as `style_id`, every one in one page. **Fetched live and cached briefly**; an empty page if the provider is unreachable, since no style is a working request.","responses":{"200":{"description":"The styles available right now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkyboxStylePage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Skybox generation is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/skybox/{generation_id}":{"get":{"operationId":"getSkybox","tags":["skybox"],"summary":"Check a skybox","parameters":[{"schema":{"type":"string"},"required":true,"name":"generation_id","in":"path"}],"responses":{"200":{"description":"The generation — the environment once it succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The capability's provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/generations":{"get":{"operationId":"listGenerations","tags":["generations"],"summary":"List generations","description":"Everything your organisation has asked any capability for, newest first, in flight or finished — filtered to the capabilities this key holds. `capability` narrows to one; `status` to one state. Paginated by cursor.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":30,"description":"How many to answer, 1 to 100."},"required":false,"description":"How many to answer, 1 to 100.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["tts","image","video","stt","skybox"],"description":"Only generations of this capability"},"required":false,"description":"Only generations of this capability","name":"capability","in":"query"},{"schema":{"type":"string","enum":["queued","submitted","running","succeeded","failed","moderated"],"description":"Only generations in this state"},"required":false,"description":"Only generations in this state","name":"status","in":"query"}],"responses":{"200":{"description":"One page of generations, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationPage"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/generations/{generation_id}":{"get":{"operationId":"getGeneration","tags":["generations"],"summary":"Read one generation","description":"Any capability's generation by its `gen_…` id, in the shape the capability's own poll answers. One made under a scope this key does not hold is a 404.","parameters":[{"schema":{"type":"string"},"required":true,"name":"generation_id","in":"path"}],"responses":{"200":{"description":"The generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/transcriptions":{"post":{"operationId":"createTranscription","tags":["transcription"],"summary":"Transcribe audio, queued","description":"Accepts the request, charges an estimate and returns immediately with a `gen_…` id. Poll `GET /v1/transcriptions/{id}` until `status` is `succeeded` or `failed`.\n\n**Name a recording in your asset library** with `asset_id` — upload it first with `POST /v1/assets/upload-url`. The audio is kept: transcribing it twice costs one upload.\n\n**Use `/v1/stt` instead if the audio is short.** This route is for what a queue makes possible: files up to the platform's `limits.uploads.audio_bytes` (`GET /v1/platform/limits`), audio of any length, and a result that lands in your library as JSON with the word timestamps in it.\n\n**The charge is an estimate, then a correction.** An asset that has finished processing carries its measured length, so that is what you are quoted; either way it is reconciled against the provider's own measurement, and `transcript.duration_seconds` on the finished generation is the figure that was charged. A generation that fails is refunded in full.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscribeAssetRequest"}}}},"responses":{"202":{"description":"Accepted and charged. Poll `GET /v1/transcriptions/{id}`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"That asset is not audio, failed to process, or has no stored audio; or a folder that is not one","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"That asset is still being processed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Object storage or the provider is not configured here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/transcriptions/{generation_id}":{"get":{"operationId":"getTranscription","tags":["transcription"],"summary":"Check a transcription","description":"Poll every couple of seconds until `status` is terminal. On success `transcript.text` carries the text inline; `transcript.url` is a short-lived link to the full provider response as JSON, including word timestamps and speaker labels where they were requested. That JSON is also in your asset library under `transcript.asset_id`, which is where to fetch it from later.","parameters":[{"schema":{"type":"string"},"required":true,"name":"generation_id","in":"path"}],"responses":{"200":{"description":"The generation — the transcript once `succeeded`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates":{"get":{"operationId":"listTemplates","tags":["templates"],"summary":"List templates","description":"Every published template, with the inputs each one takes and what a generation from it costs your organisation.\n\nA template is a generation somebody has already designed — the prompt, the size, the style, every parameter — leaving some of its inputs for you to fill: a picture or two, or the prompt itself. `capability` says what it makes and which scope your key needs; `inputs` says what to supply, and each one's `kind` says whether that is an asset id or text. Every template is in one page.","responses":{"200":{"description":"The published templates, priced for your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatePage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates/{template_id}":{"get":{"operationId":"getTemplate","tags":["templates"],"summary":"Read a template","parameters":[{"schema":{"type":"string"},"required":true,"name":"template_id","in":"path"}],"responses":{"200":{"description":"The template, priced for your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such template","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates/{template_id}/generate":{"post":{"operationId":"generateFromTemplate","tags":["templates"],"summary":"Generate from a template","description":"Fill the template's inputs and it generates exactly what its author designed. Accepted, charged and returned immediately with a `gen_…` id — poll it on the endpoint for the template's `capability`: `GET /v1/images/{id}`, `GET /v1/videos/{id}` or `GET /v1/skybox/{id}`.\n\n**The key needs the capability's scope**, not a scope of its own: a key that may make images may use image templates. **Charged when accepted**, at the price the listing showed, and refunded in full if the generation fails.\n\n`inputs` names each slot the template opens and what fills it: the `ast_…` asset in your library for an `image` input, your text for a `text` one. A required input left out, an input the template does not open, or a picture slot given something that is not an asset id is a 422 before anything is charged; an asset that is missing, is not yours or is not an image is a 400, likewise before anything is charged.\n\n`parameters` overrides the ones the template lists in `exposed`, by name; anything else is decided by the template and a 422 if sent. **An exposed size, duration or resolution changes the price**: the listing quotes the defaults, and the 202 says what was charged. **So do your own pictures on an image template**: each is charged on top of the listing's price, at the output's per-megapixel rate and the size sent.\n\n**Reference images are sent at the largest size that fits.** An image template shares the provider's 9 megapixel budget between its output, the pictures it fixes and your references, so a phone photograph is sent at a smaller copy when the original would not fit — you never have to pick the size.","parameters":[{"schema":{"type":"string"},"required":true,"name":"template_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateGenerateRequest"}}}},"responses":{"202":{"description":"Accepted and charged. Poll the capability's own endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfo"}}}},"400":{"description":"An input names an asset that is missing, is not yours, is not an image, or is still being processed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such template","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates/{template_id}/files/{name}":{"get":{"operationId":"getTemplateFile","tags":["templates"],"summary":"A template's example image","description":"Public — every tenant sees the same templates. `name` is `example` for the picture of what the template produces, or `input-<slot>` for the example beside one of its inputs; the URLs come from the template itself and are not worth constructing by hand. `v` is the token in those URLs; supplying it makes the response immutable-cacheable.","parameters":[{"schema":{"type":"string"},"required":true,"name":"template_id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string"},"required":false,"name":"v","in":"query"}],"responses":{"200":{"description":"The image, or a redirect to where object storage serves it","content":{"application/json":{"schema":{}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such template or file"}}}},"/v1/assets":{"get":{"operationId":"listAssets","tags":["assets"],"summary":"List assets","description":"Everything your organisation's services have produced, newest first unless `sort` and `order` say otherwise. Paginated by cursor: pass the `next_cursor` from one response as `cursor` on the next, with the same `sort`, and stop when it comes back null.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["input","output"]},"required":false,"name":"kind","in":"query"},{"schema":{"type":"string","enum":["audio","image","video","data"]},"required":false,"name":"media_type","in":"query"},{"schema":{"type":"string","enum":["tts","image","video","stt","skybox"],"description":"Which capability produced it"},"required":false,"description":"Which capability produced it","name":"capability","in":"query"},{"schema":{"type":"string","description":"`req_…` — one call's output"},"required":false,"description":"`req_…` — one call's output","name":"request_id","in":"query"},{"schema":{"type":"string","maxLength":120,"description":"Match anywhere in the name, whatever the case"},"required":false,"description":"Match anywhere in the name, whatever the case","name":"search","in":"query"},{"schema":{"type":"string","enum":["created_at","name","size_bytes","duration_seconds"],"default":"created_at"},"required":false,"name":"sort","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc"},"required":false,"name":"order","in":"query"}],"responses":{"200":{"description":"One page of the library, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetPage"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/assets/upload-url":{"post":{"operationId":"createAssetUploadUrl","tags":["assets"],"summary":"Ask where to upload a file","description":"Returns a short-lived URL to `PUT` the file to, with the `Content-Type` you name, and the `asset_id` to confirm it with afterwards. **The bytes never pass through this API.**\n\nNothing is created by this call. Confirm with `POST /v1/assets/uploaded` once the `PUT` succeeds.\n\nAn image may weigh up to the platform's `limits.uploads.image_bytes`, a recording its `limits.uploads.audio_bytes` (`GET /v1/platform/limits`); the confirm refuses a larger file.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadTicketRequest"}}}},"responses":{"200":{"description":"Where to `PUT` the bytes, and until when","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadTicket"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The library holds the platform's `limits.org.asset_uploads` uploaded files","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Object storage is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/assets/uploaded":{"post":{"operationId":"confirmAssetUpload","tags":["assets"],"summary":"Confirm an uploaded file","description":"Call this once the `PUT` from `upload-url` has succeeded. The stored object's size and type are checked, the asset row is created and the conversion is queued. The asset comes back `pending`; poll `GET /v1/assets/{id}` until it is `ready`, or `failed` with the reason in `error`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadConfirm"}}}},"responses":{"202":{"description":"Accepted. The asset is `pending` until it converts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The upload has not arrived — the `PUT` has not landed — or it was confirmed already","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Object storage is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/assets/delete":{"post":{"operationId":"deleteAssets","tags":["assets"],"summary":"Delete several assets","description":"Needs `assets:write`. Soft-deletes a selection and reports how many rows it changed; ids that are not yours, or already deleted, are skipped.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetBulkRequest"}}}},"responses":{"200":{"description":"How many assets were removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetsAffected"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/assets/move":{"post":{"operationId":"moveAssets","tags":["assets"],"summary":"Move several assets","description":"Needs `assets:write`. Into one folder, or to the root with `folder_id: null`; ids that are not yours are skipped.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetMoveRequest"}}}},"responses":{"200":{"description":"How many assets moved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetsAffected"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/assets/{asset_id}":{"get":{"operationId":"getAsset","tags":["assets"],"summary":"Get one asset","description":"Includes a time-limited `url` for the bytes.","parameters":[{"schema":{"type":"string"},"required":true,"name":"asset_id","in":"path"}],"responses":{"200":{"description":"The asset, with signed links to its content and variants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateAsset","tags":["assets"],"summary":"Rename or move an asset","description":"Needs `assets:write`. `folder_id` is tri-state: omitted leaves it alone, `null` moves the asset to the root, and an id moves it there.","parameters":[{"schema":{"type":"string"},"required":true,"name":"asset_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetUpdate"}}}},"responses":{"200":{"description":"The asset as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteAsset","tags":["assets"],"summary":"Delete an asset","description":"Needs `assets:write`. Removes the asset from the library, and its bytes — every variant, the thumbnail — from storage shortly after.","parameters":[{"schema":{"type":"string"},"required":true,"name":"asset_id","in":"path"}],"responses":{"204":{"description":"Deleted"},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/asset-folders":{"get":{"operationId":"listAssetFolders","tags":["assets"],"summary":"List folders at one level","description":"One level of the tree — the top level unless `parent_id` names a folder — alphabetically, with what each holds. A level holds at most 500 folders, so it is always one page: `next_cursor` is null.","parameters":[{"schema":{"type":"string","description":"`fld_…`; omit it for the top level"},"required":false,"description":"`fld_…`; omit it for the top level","name":"parent_id","in":"query"}],"responses":{"200":{"description":"The folders at that level","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderPage"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createAssetFolder","tags":["assets"],"summary":"Create a folder","description":"Needs `assets:write`. At the top level, or under `parent_id`. Two siblings cannot share a name (409).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderCreate"}}}},"responses":{"201":{"description":"The new folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderSummary"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A folder beside it already has that name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/asset-folders/{folder_id}":{"get":{"operationId":"getAssetFolder","tags":["assets"],"summary":"Get one folder and its trail","parameters":[{"schema":{"type":"string"},"required":true,"name":"folder_id","in":"path"}],"responses":{"200":{"description":"The folder and its ancestors","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateAssetFolder","tags":["assets"],"summary":"Rename or move a folder","description":"Needs `assets:write`. `parent_id` is tri-state: omitted leaves it, `null` moves it to the top level.","parameters":[{"schema":{"type":"string"},"required":true,"name":"folder_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderUpdate"}}}},"responses":{"200":{"description":"The folder as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderSummary"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteAssetFolder","tags":["assets"],"summary":"Delete a folder","description":"Needs `assets:write`. Its contents move up to the parent; nothing is deleted but the folder itself.","parameters":[{"schema":{"type":"string"},"required":true,"name":"folder_id","in":"path"}],"responses":{"200":{"description":"What moved up to the parent; the folder itself is gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderDeleted"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such asset or folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/sessions":{"post":{"operationId":"createSession","tags":["agent"],"summary":"Open or continue a conversation","description":"**Called with the end user's ephemeral key** — the conversation is always that person's, and `end_user_id`, if sent, must name them.\n\n**It continues the person's most recent conversation with this agent, if there is one young enough to continue** — the platform's `agents.sessions.resume_max_age_days` (`GET /v1/platform/limits`), unless the organisation sets its own. Send `resume: false` to always start a new one — that is what a \"new chat\" button should do. The response's `resumed` says which happened, and a continued conversation arrives with its `messages` already populated and its `turn_seq` past zero.\n\n**Editing the agent reaches this conversation too**, within about a minute, from its next turn onward.\n\n**This call makes no model call and no speech call**, and a new conversation comes back with no messages — even for an agent that has a `first_message`. The opening line is the agent's first turn on `/v1/agent/stream`, written when a client connects, and streamed and spoken a sentence at a time like every other reply. The socket also decides *whether* to greet, and `ready.greeting` says which.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionCreateRequest"}}}},"responses":{"201":{"description":"The conversation — new or continued, per `resumed`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key's scopes do not cover agents, or it is for another agent or person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listSessions","tags":["agent"],"summary":"List conversations","description":"Most recently used first, a page at a time. Messages are not included — read one session for its transcript.\n\n**The key's own end user and nobody else**, and only conversations with the agents the key may talk to.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page of conversations, most recent first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/sessions/{session_id}":{"get":{"operationId":"getSession","tags":["agent"],"summary":"Read a conversation","description":"The transcript, oldest first, exactly as stored — including every turn from before a compaction. **Compaction shortens what the model is sent, never what you can read back.**\n\n**The most recent `limit` messages**, because what a client renders is the end of a conversation.\n\n**To read it all, page backwards**: pass `messages.next_cursor` as `cursor`; it is null once you have reached the start.\n\nEach message carries the `mood` the agent reported when it wrote it, if the agent reports moods.","parameters":[{"schema":{"type":"string"},"required":true,"name":"session_id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"How many messages, counting back from the newest, 1 to 200. The page itself is oldest first."},"required":false,"description":"How many messages, counting back from the newest, 1 to 200. The page itself is oldest first.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"`messages.next_cursor` of the page before, for the older messages."},"required":false,"description":"`messages.next_cursor` of the page before, for the older messages.","name":"cursor","in":"query"}],"responses":{"200":{"description":"The conversation and one page of its messages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDetail"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown session id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteSession","tags":["agent"],"summary":"Delete a conversation","description":"Deletes every message in it too. This cannot be undone.","parameters":[{"schema":{"type":"string"},"required":true,"name":"session_id","in":"path"}],"responses":{"204":{"description":"The session and its messages are gone"},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown session id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/sessions/{session_id}/compact":{"post":{"operationId":"compactSession","tags":["agent"],"summary":"Compact a conversation","description":"Summarises everything said so far but the last few exchanges and starts the next turn from that summary, then those exchanges word for word, instead of the full history. How many messages stay is the platform's `agents.memory.compaction.keep_messages`; a conversation no longer than that is summarised whole.\n\nThe transcript is **not** changed — `GET /v1/agent/sessions/{id}` still returns every message. What changes is only what the model is sent.\n\nThis is a model call, charged to the key. Agents can also do it automatically by setting a token threshold.","parameters":[{"schema":{"type":"string"},"required":true,"name":"session_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompactRequest"}}}},"responses":{"200":{"description":"The summary that replaced the turns it covers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompactionInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The key has spent its credit — mint another; nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown session id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Another compaction of this conversation finished first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Nothing new to compact since the last compaction, or the model's summary was empty or no shorter than what it would replace; nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The model provider failed or timed out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The model provider is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/sessions/{session_id}/extract":{"post":{"operationId":"extractSessionFacts","tags":["agent"],"summary":"Extract what is worth remembering","description":"Compacts the conversation **and** extracts durable facts about the user into long-term memory, in one model call, charged to the key.\n\nIdentical to `POST /sessions/{id}/compact` with `{\"extract_facts\": true}` — it exists because remembering and shortening are different intentions even though they are one request.","parameters":[{"schema":{"type":"string"},"required":true,"name":"session_id","in":"path"}],"responses":{"200":{"description":"The facts pulled out of the conversation and stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompactionInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The key has spent its credit — mint another; nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown session id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Another compaction of this conversation finished first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Nothing new to compact since the last compaction, or the model's summary was empty or no shorter than what it would replace; nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The model provider failed or timed out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The model provider is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/facts":{"get":{"operationId":"listFacts","tags":["agent"],"summary":"What is remembered about an end user","description":"What is stored about the key's end user, **oldest first**, a page at a time — up to 200 a page, 100 by default. A fact learnt while you page lands on the last page.\n\nFacts are shared across every agent you run — the same person talking to a different character is still the same person. `agent_id` records which one was being talked to when a fact was learned.\n\nA fact a later conversation changed or contradicted is **replaced**: hidden from the agents and from this list, and deleted the platform's `agents.memory.facts.replaced_kept_days` later (`GET /v1/platform/limits`). `include_replaced=true` lists those too, each with `replaced_at`, and `replaced_by` naming the fact that took its place.","parameters":[{"schema":{"type":"string","description":"Optional, and only ever the key's own end user — their `eu_...` id or your own id for them. Anybody else is a 403."},"required":false,"description":"Optional, and only ever the key's own end user — their `eu_...` id or your own id for them. Anybody else is a 403.","name":"end_user_id","in":"query"},{"schema":{"type":"boolean","default":false,"description":"Also list the facts a later conversation replaced or removed, until they are deleted, the platform's `agents.memory.facts.replaced_kept_days` later (`GET /v1/platform/limits`)."},"required":false,"description":"Also list the facts a later conversation replaced or removed, until they are deleted, the platform's `agents.memory.facts.replaced_kept_days` later (`GET /v1/platform/limits`).","name":"include_replaced","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":100,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"}],"responses":{"200":{"description":"What the platform remembers about this end user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createFact","tags":["agent"],"summary":"Remember something about the end user","description":"Adds a fact about the key's end user, as you state it, for the agents to know from their next turn: what your app already knows and wants them to keep — a name, a preference.\n\n**It is yours**: a later conversation confirms it but never replaces or removes it, and its `source` is `customer`. With `agent_id` only that agent recalls it; without, every agent of yours does. Free.\n\n**Idempotent by its words**: a fact that already says the same thing is answered `200` — now yours — and a new one `201`. A fact forgotten before is remembered again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactCreateRequest"}}}},"responses":{"200":{"description":"The fact that already said it, now yours","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactInfo"}}}},"201":{"description":"The fact, remembered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key's scopes do not cover this, or it is pinned to agents and `agent_id` names another","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No text, or text that looks like a secret — a password, a code, a card, account or identity number, or a key — which no fact may hold","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteAllFacts","tags":["agent"],"summary":"Forget an end user entirely","description":"Deletes everything remembered about the key's end user. Cannot be undone.\n\nIt leaves their conversations and the end user itself alone. `DELETE /v1/end-users/{id}` removes all three, and is what to call when one of your users asks to be forgotten outright.","parameters":[{"schema":{"type":"string","description":"Optional, and only ever the key's own end user — their `eu_...` id or your own id for them. Anybody else is a 403."},"required":false,"description":"Optional, and only ever the key's own end user — their `eu_...` id or your own id for them. Anybody else is a 403.","name":"end_user_id","in":"query"}],"responses":{"200":{"description":"How many stored facts were deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactsDeleted"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/facts/{fact_id}":{"patch":{"operationId":"updateFact","tags":["agent"],"summary":"Correct what is remembered","description":"Rewrites one fact about the key's end user. It becomes yours (`source` `customer`), so a later conversation never replaces it, and **the old words are kept as forgotten**: no reading of what was said before brings them back.\n\nA fact a later conversation already replaced is a 409 — correct the one that replaced it — and so are words another fact already says. Free.","parameters":[{"schema":{"type":"string"},"required":true,"name":"fact_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactUpdateRequest"}}}},"responses":{"200":{"description":"The fact, corrected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown fact id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Replaced by a later conversation, or another fact already says that","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No text, or text that looks like a secret — a password, a code, a card, account or identity number, or a key — which no fact may hold","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteFact","tags":["agent"],"summary":"Forget one fact","description":"Deletes a single memory of the key's end user. Cannot be undone.","parameters":[{"schema":{"type":"string"},"required":true,"name":"fact_id","in":"path"}],"responses":{"204":{"description":"The fact is gone"},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown fact id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/relations":{"get":{"operationId":"listRelations","tags":["agent"],"summary":"How the agents feel about an end user","description":"One row per agent the key's end user has talked to that keeps a relationship, most recently active first — or, with `agent_id`, that agent's row alone.\n\n`warmth` and `trust` are moved by the agent itself when something in a conversation changes its view; `turns` and `conversations` are counted. `summary` is the sentence the model is actually given.\n\n**Per end user**: each person's relationships live with that person, so one call reads one person's.","parameters":[{"schema":{"type":"string","description":"Only this agent's relationship."},"required":false,"description":"Only this agent's relationship.","name":"agent_id","in":"query"},{"schema":{"type":"string","description":"Optional, and only ever the key's own end user — their `eu_...` id or your own id for them. Anybody else is a 403."},"required":false,"description":"Optional, and only ever the key's own end user — their `eu_...` id or your own id for them. Anybody else is a 403.","name":"end_user_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"}],"responses":{"200":{"description":"How each agent feels about the person, most recent first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelationPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents":{"get":{"operationId":"listAgents","tags":["agents"],"summary":"List agents","description":"Every agent in your organisation, with how much each has been talked to. `id` is what `POST /v1/agent/sessions` takes.\n\nNeeds only the `agent` scope: an integration that may open a conversation must be able to find the agents it may open one with.","responses":{"200":{"description":"Every agent, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createAgent","tags":["agents"],"summary":"Create an agent","description":"Needs `agent:write`.\n\nThe five configuration fields are prose and are composed into one system instruction, in a fixed order. Leave any of them empty and that section is omitted entirely rather than sent blank.\n\n`first_message` is what the agent says first - unless `first_message_generated` is on, in which case it is the *direction* for what the agent says first and the model writes the line fresh.\n\n**Either form is delivered as the agent's first turn on the WebSocket**, not when a session opens - so it streams and is spoken a sentence at a time.\n\n**The row is checked as a whole**: a `thinking_level` the model refuses, a generated opening with no direction, or a speech schedule out of order is a 422 naming the problem.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentCreateRequest"}}}},"responses":{"201":{"description":"The agent, ready to open sessions with","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The organisation already has as many agents as its plan allows. Delete one, or ask for a higher limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents/settings":{"get":{"operationId":"getAgentSettings","tags":["agents"],"summary":"Your organisation's agent defaults","description":"What every agent falls back to when it sets nothing of its own - the chain is agent, then organisation, then the built-in default. The built-in text comes back alongside, so a client can show what leaving a field empty actually means.","responses":{"200":{"description":"The defaults, and the built-in text behind each","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSettingsInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"operationId":"updateAgentSettings","tags":["agents"],"summary":"Set your organisation's agent defaults","description":"Needs `agent:write`.\n\nA partial update: omitted fields are left alone, and an explicit `null` clears one back to the built-in default.\n\n**Conversations already open pick this up**: a change here reaches every conversation in the organisation within about a minute, from its next message after that, exactly as editing an agent does. Agents that set their own value are unaffected.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSettingsUpdate"}}}},"responses":{"200":{"description":"The defaults as they now stand","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSettingsInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents/{agent_id}":{"get":{"operationId":"getAgent","tags":["agents"],"summary":"Read an agent","parameters":[{"schema":{"type":"string"},"required":true,"name":"agent_id","in":"path"}],"responses":{"200":{"description":"The agent's full configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateAgent","tags":["agents"],"summary":"Update an agent","description":"Needs `agent:write`.\n\nA partial update: fields you omit are left alone, and an explicit `null` clears one.\n\n**Conversations already open pick this up within about a minute**, at their next turn after the change reaches them, so there is nothing to reopen - and equally, an edit made while somebody is talking to the agent changes what they are talking to.\n\n**The row is checked as a whole**: a `thinking_level` the agent's model refuses, a generated opening with no direction, or a speech schedule out of order is a 422 naming the problem, whichever field the request changed. Send `expected_version` to be refused with 409 if somebody else changed the agent since you read it.","parameters":[{"schema":{"type":"string"},"required":true,"name":"agent_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentUpdate"}}}},"responses":{"200":{"description":"The agent as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`expected_version` was sent and the agent has been changed since. Nothing was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteAgent","tags":["agents"],"summary":"Delete an agent","description":"Needs `agent:write`.\n\nAlso deletes every session and message belonging to it. This cannot be undone.","parameters":[{"schema":{"type":"string"},"required":true,"name":"agent_id","in":"path"}],"responses":{"204":{"description":"The agent is gone"},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/end-users":{"post":{"operationId":"createEndUser","tags":["end users"],"summary":"Create an end user","description":"Somebody your agents will talk to.\n\n**Send your own `external_id` and this is idempotent** — the same value always returns the same person, created only the first time. That is the recommended way to use it: pass your user id at every sign-in and you never have to store the `eu_...` we mint, while their memory and every relationship stay attached.\n\nSend no `external_id` and you get a fresh person each call, and **must** keep the returned id — it is the only way back to what is remembered about them.\n\n**Make the person before their key**: an ephemeral key is minted for one end user, and a conversation is opened with a key.\n\n**Your plan caps how many end users you hold**: past it, a new one is a 409 naming the limit, while a repeat of an existing `external_id` is answered as ever.\n\n**A repeat call answers `200` rather than `201`**, so the two cases are distinguishable without comparing ids.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserCreateRequest"}}}},"responses":{"200":{"description":"The end user this `external_id` already named","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserInfo"}}}},"201":{"description":"The end user. The same `external_id` always returns the same one","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The organisation already holds as many end users as its plan allows. Delete one, or move to a larger plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listEndUsers","tags":["end users"],"summary":"List end users","description":"Newest first.\n\n**End users once minted for a session are hidden unless you ask for them** with `include_generated=true`: a conversation opened without an `end_user_id` used to create one. None is minted any more - a conversation needs a key, and a key its end user.","parameters":[{"schema":{"type":"boolean","default":false,"description":"Also list the end users once minted for a session opened without one."},"required":false,"description":"Also list the end users once minted for a session opened without one.","name":"include_generated","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page of end users, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/end-users/{end_user_id}":{"get":{"operationId":"getEndUser","tags":["end users"],"summary":"Read an end user","description":"`end_user_id` may be the `eu_...` we minted **or** your own `external_id` — the two are interchangeable everywhere one is accepted.","parameters":[{"schema":{"type":"string"},"required":true,"name":"end_user_id","in":"path"}],"responses":{"200":{"description":"The end user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown end user id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateEndUser","tags":["end users"],"summary":"Rename an end user, or change your id for them","description":"Absent fields are untouched; `null` clears one.\n\n**There is no field for the `eu_...` id.** Long-term memory is filed under it, so rewriting it would strand everything remembered about this person. Your `external_id` is the identifier you can change.","parameters":[{"schema":{"type":"string"},"required":true,"name":"end_user_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserUpdateRequest"}}}},"responses":{"200":{"description":"The updated end user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown end user id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Another end user in this organisation already has that `external_id`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteEndUser","tags":["end users"],"summary":"Delete an end user and everything about them","description":"Removes the end user, **every conversation they have had**, and everything remembered about them. Cannot be undone.\n\nThis is what to call when one of your users asks to be forgotten. `DELETE /v1/agent/facts` erases only the memory and leaves the conversations; this erases both.\n\nThe id is not reissued, and nothing that was filed under it is reachable again.","parameters":[{"schema":{"type":"string"},"required":true,"name":"end_user_id","in":"path"}],"responses":{"200":{"description":"The end user and every conversation with them are gone, counted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserDeleted"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown end user id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/end-users/{end_user_id}/facts":{"get":{"operationId":"listEndUserFacts","tags":["end users"],"summary":"What is remembered about an end user","description":"The person's facts, **oldest first**, a page at a time — what `GET /v1/agent/facts` answers their own key, here for your server with no key to mint. `include_replaced=true` lists those a later conversation replaced or removed too, until they are deleted.","parameters":[{"schema":{"type":"string"},"required":true,"name":"end_user_id","in":"path"},{"schema":{"type":"boolean","default":false,"description":"Also list the facts a later conversation replaced or removed, until they are deleted, the platform's `agents.memory.facts.replaced_kept_days` later (`GET /v1/platform/limits`)."},"required":false,"description":"Also list the facts a later conversation replaced or removed, until they are deleted, the platform's `agents.memory.facts.replaced_kept_days` later (`GET /v1/platform/limits`).","name":"include_replaced","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":100,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page of what is remembered, oldest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown end user id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createEndUserFact","tags":["end users"],"summary":"Tell the agents something about an end user","description":"Adds a fact about the person, as you state it, for the agents to know from their next turn — what your records already hold: a name, a plan, a preference. It works before they have ever talked to an agent.\n\n**It is yours** (`source` `customer`): a later conversation confirms it but never replaces or removes it. With `agent_id` only that agent recalls it. **Idempotent by its words**: a fact already saying it is answered `200`, now yours, and a new one `201`. Free.","parameters":[{"schema":{"type":"string"},"required":true,"name":"end_user_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactCreateRequest"}}}},"responses":{"200":{"description":"The fact that already said it, now yours","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactInfo"}}}},"201":{"description":"The fact, remembered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown end user id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No text, or text that looks like a secret — a password, a code, a card, account or identity number, or a key — which no fact may hold","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/end-users/{end_user_id}/facts/{fact_id}":{"patch":{"operationId":"updateEndUserFact","tags":["end users"],"summary":"Correct what is remembered about an end user","description":"Rewrites one fact. It becomes yours (`source` `customer`), so a later conversation never replaces it, and **the old words are kept as forgotten**: no reading of what was said before brings them back. A fact already replaced, or words another fact already says, are a 409. Free.","parameters":[{"schema":{"type":"string"},"required":true,"name":"end_user_id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"fact_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactUpdateRequest"}}}},"responses":{"200":{"description":"The fact, corrected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown end user id, or Unknown fact id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Replaced by a later conversation, or another fact already says that","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No text, or text that looks like a secret — a password, a code, a card, account or identity number, or a key — which no fact may hold","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteEndUserFact","tags":["end users"],"summary":"Forget one fact about an end user","description":"Deletes one fact; no reading of what was said before brings it back. Their conversations stay — `DELETE /v1/end-users/{id}` removes the person and everything about them. Cannot be undone.","parameters":[{"schema":{"type":"string"},"required":true,"name":"end_user_id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"fact_id","in":"path"}],"responses":{"204":{"description":"The fact is gone"},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown end user id, or Unknown fact id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/categories":{"get":{"operationId":"listKnowledgeCategories","tags":["knowledge"],"summary":"Categories in use","description":"Every category your organisation has put on an entry. Not narrowed by any filter — a picker that only offers what survives the current selection empties itself as soon as it is used.","responses":{"200":{"description":"Every category, alphabetically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeCategoryPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/insights":{"get":{"operationId":"getKnowledgeInsights","tags":["knowledge"],"summary":"What the knowledge is used for","description":"Which entries your agents drew on, by how many replies, and the newest questions whose lookups found nothing — the gaps worth filling. Counted from what each reply recorded, over the last `days` (1-90, default 7). An agent with its knowledge in its prompt looks nothing up, so it does not appear here.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":90,"default":7},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"Use per entry, and the unanswered questions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInsights"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/search":{"post":{"operationId":"searchKnowledge","tags":["knowledge"],"summary":"Test a question against the knowledge","description":"Runs one question exactly as an agent's lookup would — the same embedding, the same index, the same relevance floor — and returns the pieces it would be handed, with their scores. Name `agent_id` to search what that agent has; omit it to search every entry. Costs nothing and changes nothing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchRequest"}}}},"responses":{"200":{"description":"What a lookup would find","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchResult"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"`agent_id` is not one of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Knowledge search is not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge":{"get":{"operationId":"listKnowledge","tags":["knowledge"],"summary":"List or search knowledge","description":"Every entry in your organisation, most recently changed first, with the agents each is assigned to, a page at a time.\n\n`q` is a plain text match over title, summary and body — **not** the semantic search the agent uses. This is for finding an entry somebody wrote; the agent's own lookup answers a question by meaning.\n\n`agent_id`, `category` and `status` narrow further and combine. `status` is about the latest body: `pending` while it is indexed, `ready` once it is, `failed` with the reason in `error`. `searchable` says whether an agent can find the entry at all — an edited entry stays searchable at its previous version while the new one indexes, and keeps it if the new one fails.","parameters":[{"schema":{"type":"string","maxLength":200,"description":"A plain text match over title, summary and body."},"required":false,"description":"A plain text match over title, summary and body.","name":"q","in":"query"},{"schema":{"type":"string","description":"Only the entries this agent has."},"required":false,"description":"Only the entries this agent has.","name":"agent_id","in":"query"},{"schema":{"type":"string","maxLength":120},"required":false,"name":"category","in":"query"},{"schema":{"type":"string","enum":["pending","ready","failed","importing"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"How many to answer, 1 to 200."},"required":false,"description":"How many to answer, 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"The `next_cursor` of the page before."},"required":false,"description":"The `next_cursor` of the page before.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page of entries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgePage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createKnowledge","tags":["knowledge"],"summary":"Write a knowledge entry","description":"Needs `agent:write`. Counts against your rate limit like a generation, because indexing is model work.\n\nCreates an entry for your organisation and queues it for indexing. It comes back `pending` and **no agent can find it yet** — poll `GET /v1/knowledge/{id}` until `status` is `ready`, or `failed` with the reason in `error`.\n\n`agent_ids` assigns it to any number of agents at once and may be empty — an entry nobody has yet is a draft, not a fault. Assigning later is `PUT /v1/knowledge/{id}/agents`. Ids that are not yours are skipped rather than failing the write.\n\n`summary` is normally left out: the indexer writes a one-line summary for the agent's prompt, which is how it knows a lookup is worth making.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeCreate"}}}},"responses":{"201":{"description":"The entry, `pending` until indexed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The organisation already has as many entries as the platform allows. Delete one before adding another.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/bulk":{"post":{"operationId":"bulkEditKnowledge","tags":["knowledge"],"summary":"Delete, assign or unassign many entries at once","description":"Needs `agent:write`.\n\n`delete` removes up to 200 entries for every agent that had them. `assign` and `unassign` add the named agents to each entry, or take them away, and leave each entry's other agents alone — unlike `PUT /v1/knowledge/{entry_id}/agents`, which replaces one entry's set. Ids that are not yours come back in `skipped`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeBulk"}}}},"responses":{"200":{"description":"How many entries were acted on, and what changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeBulkResult"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/urls":{"post":{"operationId":"createKnowledgeFromUrl","tags":["knowledge"],"summary":"Make a knowledge entry from a web page","description":"Needs `agent:write`. The page is fetched and read into text, then indexed; it comes back `importing`. A PDF, Word or text file online works the same way. `refresh` reads it again daily or weekly and re-indexes it when it has changed. Only the public internet is reached.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeUrlCreate"}}}},"responses":{"202":{"description":"The entry, `importing`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The organisation's entry limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/upload-url":{"post":{"operationId":"createKnowledgeUploadUrl","tags":["knowledge"],"summary":"Ask where to upload a file for knowledge","description":"Needs `agent:write`. Returns a short-lived URL to `PUT` the file to, the `Content-Type` to send with it, and the `upload_id` to make the entry with afterwards (`POST /v1/knowledge/files`). **The bytes never pass through this API.** Up to 10 MB: PDF, Word, Excel, OpenDocument, CSV, HTML, Markdown or plain text. A CSV with `question` and `answer` columns becomes one question-and-answer pair per row.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeUploadRequest"}}}},"responses":{"200":{"description":"Where to `PUT` the file","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeUploadTicket"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Larger than 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/files":{"post":{"operationId":"createKnowledgeFromFile","tags":["knowledge"],"summary":"Make a knowledge entry from an uploaded file","description":"Needs `agent:write`. After the `PUT` to the upload URL succeeded: the entry comes back `importing` while the file is read into text, then `pending` while it is indexed, then `ready`. The text is the entry's body, editable like any other. Without a `title` the file's own is used.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeFileCreate"}}}},"responses":{"202":{"description":"The entry, `importing`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Nothing was uploaded for that upload_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The organisation's entry limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Larger than 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/{entry_id}":{"get":{"operationId":"getKnowledge","tags":["knowledge"],"summary":"Read a knowledge entry","description":"The entry, how many chunks it indexed to, and which agents have it. This is what to poll after a write until `status` is `ready`.","parameters":[{"schema":{"type":"string"},"required":true,"name":"entry_id","in":"path"}],"responses":{"200":{"description":"The entry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown knowledge entry id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateKnowledge","tags":["knowledge"],"summary":"Edit a knowledge entry","description":"Needs `agent:write`. Counts against your rate limit like a generation.\n\nA partial update: fields you omit are left alone. An explicit `null` clears `category`; on `summary` it hands the line back to the indexer, which writes a new one. A `summary` you write is kept through every later edit; one the indexer wrote is rewritten with each new body.\n\n**Only a changed `body` costs anything.** It re-indexes: `status` returns to `pending`, and **the previous version stays searchable until the new one is ready**, so no agent loses the entry meanwhile. Renaming or recategorising is immediate.\n\nOne edit reaches every agent the entry is assigned to.","parameters":[{"schema":{"type":"string"},"required":true,"name":"entry_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeUpdate"}}}},"responses":{"200":{"description":"The entry as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown knowledge entry id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`expected_version` is older than the entry: somebody changed it since.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteKnowledge","tags":["knowledge"],"summary":"Delete a knowledge entry","description":"Needs `agent:write`.\n\nRemoves the entry, everything indexed from it, and every assignment to it. **This affects every agent that had it** — unassigning one agent is `PUT /v1/agents/{agent_id}/knowledge` and leaves the entry alone.","parameters":[{"schema":{"type":"string"},"required":true,"name":"entry_id","in":"path"}],"responses":{"204":{"description":"The entry is gone, for every agent that had it"},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown knowledge entry id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/{entry_id}/reindex":{"post":{"operationId":"reindexKnowledge","tags":["knowledge"],"summary":"Index a knowledge entry again","description":"Needs `agent:write`. Counts against your rate limit like a generation.\n\nQueues the entry for indexing as it stands — for one that `failed`, say while a provider was down. `status` returns to `pending`; whatever version was searchable stays searchable until the new one is ready. Entries stuck `pending` are retried on their own after half an hour.","parameters":[{"schema":{"type":"string"},"required":true,"name":"entry_id","in":"path"}],"responses":{"202":{"description":"The entry, `pending` until indexed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown knowledge entry id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/{entry_id}/refresh":{"post":{"operationId":"refreshKnowledge","tags":["knowledge"],"summary":"Read a page-made entry again now","description":"Needs `agent:write`. For an entry made from a web page: fetches it now rather than at its next scheduled refresh, and re-indexes it if it changed.","parameters":[{"schema":{"type":"string"},"required":true,"name":"entry_id","in":"path"}],"responses":{"202":{"description":"The entry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown knowledge entry id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/knowledge/{entry_id}/agents":{"put":{"operationId":"setKnowledgeAgents","tags":["knowledge"],"summary":"Set which agents have an entry","description":"Needs `agent:write`.\n\nReplaces the entry's assignments with exactly the agents named — the mirror of `PUT /v1/agents/{agent_id}/knowledge`, which edits the same links from the other end. Declarative: the list is the complete set, and an empty one unassigns it from everybody without deleting anything. Ids that are not yours come back in `skipped`.","parameters":[{"schema":{"type":"string"},"required":true,"name":"entry_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeAgentsUpdate"}}}},"responses":{"200":{"description":"How many links were added and removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeAssigned"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown knowledge entry id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents/{agent_id}/knowledge":{"get":{"operationId":"listAgentKnowledge","tags":["agents"],"summary":"What an agent can look up","description":"The knowledge entries assigned to this agent, newest first, in one page: they are bounded by your organisation's entry limit.\n\n**Entries belong to the organisation, not to this agent.** The same one can be assigned to any number of agents, and it is written and edited on `/v1/knowledge`. This endpoint and its sibling below only say *which* an agent may read.\n\nOnly `ready` entries are visible to the agent itself. A `pending` one is still being indexed; a `failed` one carries the reason in `error`.","parameters":[{"schema":{"type":"string"},"required":true,"name":"agent_id","in":"path"}],"responses":{"200":{"description":"The entries this agent may look up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgePage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"operationId":"setAgentKnowledge","tags":["agents"],"summary":"Set which entries an agent can look up","description":"Needs `agent:write`.\n\nReplaces this agent's assignments with exactly the entries named. **Declarative, not add and remove**: the list is the complete set, so there is no sequence of calls that can end up disagreeing with itself, and an empty list unassigns everything.\n\n**Nothing is created or destroyed here.** Unassigning leaves the entry alone for every other agent that has it; deleting one is `DELETE /v1/knowledge/{entry_id}`. Ids that are not yours come back in `skipped` rather than failing the call.","parameters":[{"schema":{"type":"string"},"required":true,"name":"agent_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentKnowledgeUpdate"}}}},"responses":{"200":{"description":"How many links were added and removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeAssigned"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/tools":{"get":{"operationId":"listTools","tags":["tools"],"summary":"List tools","description":"Your organisation's tool library, newest first, in one page: at most the platform's `limits.org.tools` (`GET /v1/platform/limits`).\n\nA tool is something an agent's model may call while it replies: **`webhook`**, your server, sent a signed `POST` with the call's arguments; or **`app`**, the person's app, called over the agent's WebSocket. Each is turned on per agent (`PUT /v1/agents/{agent_id}/tools`), up to the platform's `limits.agent.tools` an agent (`GET /v1/platform/limits`).","responses":{"200":{"description":"Every tool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createTool","tags":["tools"],"summary":"Create a tool","description":"Needs `agent:write`.\n\nA webhook tool answers its **signing secret** (`whsec_…`) this once: every call carries `X-Tool-Signature: t=<unix seconds>,v1=<hex>`, the HMAC-SHA256 of `<t>.<body>` under it. Check it, and that `t` is within five minutes. `headers` are sent with every call — your own API token — and stored encrypted.\n\n**The tool is checked whole**: its `name` (what the model calls it by, unique in your organisation), its `parameters` (the JSON Schema subset the model takes), a webhook's `url` (`https://`, public, never this platform's) and its `headers` are each a 422 naming the problem.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolCreate"}}}},"responses":{"201":{"description":"The tool, with its signing secret shown this once","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolWithSecret"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A tool by that name exists already, or the library holds the platform's `limits.org.tools`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"This deployment cannot seal a tool's secret (`TOOL_SECRET_KEY` unset): webhook tools cannot be saved or called.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/tools/{tool_id}":{"get":{"operationId":"getTool","tags":["tools"],"summary":"Read a tool","parameters":[{"schema":{"type":"string"},"required":true,"name":"tool_id","in":"path"}],"responses":{"200":{"description":"The tool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolInfo"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown tool id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateTool","tags":["tools"],"summary":"Update a tool","description":"Needs `agent:write`.\n\nA partial update: omitted fields are left alone, an explicit `null` clears one, and `headers` replaces them all. `kind` is fixed.\n\n**Conversations already open pick this up within about a minute**, at their next turn after the change reaches them. Send `expected_version` to be refused with 409 if somebody else changed the tool since you read it.","parameters":[{"schema":{"type":"string"},"required":true,"name":"tool_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolUpdate"}}}},"responses":{"200":{"description":"The tool as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolInfo"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown tool id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`expected_version` was sent and the tool has been changed since, or the new name is taken. Nothing was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"This deployment cannot seal a tool's secret (`TOOL_SECRET_KEY` unset): webhook tools cannot be saved or called.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteTool","tags":["tools"],"summary":"Delete a tool","description":"Needs `agent:write`.\n\nTurns it off for every agent that had it; conversations already open stop offering it within about a minute. This cannot be undone.","parameters":[{"schema":{"type":"string"},"required":true,"name":"tool_id","in":"path"}],"responses":{"204":{"description":"The tool is gone"},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown tool id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/tools/{tool_id}/calls":{"get":{"operationId":"listToolCalls","tags":["tools"],"summary":"A tool's recent calls","description":"Its most recent calls in your agents' conversations, newest first, from the past seven days: when, in which conversation and for whom, how long each took, and what failed. **No inputs or answers**: those are each turn's, in the conversation's history (`tool_calls`). A call shows here within about a minute. **Sampled at volume**: a tool called thousands of times an hour keeps a share of its calls here, never all — never a count to bill on. `next_cursor` is always `null`: ask for up to 100 with `limit`. Answers 503 while this deployment cannot read call logs.","parameters":[{"schema":{"type":"string"},"required":true,"name":"tool_id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"How many of the most recent calls, 1 to 100."},"required":false,"description":"How many of the most recent calls, 1 to 100.","name":"limit","in":"query"}],"responses":{"200":{"description":"Its most recent calls, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolCallPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown tool id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The call log did not answer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"This deployment cannot read call logs yet (`CF_ACCOUNT_ID`, and the admin panel's analytics token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/tools/{tool_id}/secret":{"post":{"operationId":"rotateToolSecret","tags":["tools"],"summary":"Rotate a tool's signing secret","description":"Needs `agent:write`.\n\nA new signing secret, **shown this once**; the old one stops signing within about a minute, as conversations pick the change up. A webhook tool only.","parameters":[{"schema":{"type":"string"},"required":true,"name":"tool_id","in":"path"}],"responses":{"200":{"description":"The tool, with its new signing secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolWithSecret"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown tool id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"This deployment cannot seal a tool's secret (`TOOL_SECRET_KEY` unset): webhook tools cannot be saved or called.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/tools/{tool_id}/test":{"post":{"operationId":"testTool","tags":["tools"],"summary":"Call a tool once, as a test","description":"Needs `agent:write`.\n\nSends your server one call, exactly as a conversation would — signed, with your headers, under the tool's timeout — carrying the `arguments` you give and `test: true`, with no agent, conversation or person. Answers what came back as the model would read it: the JSON, or the text, cut at the platform's `agents.tools.result_max_bytes` (`GET /v1/platform/limits`), or what failed. A webhook tool only; nothing is charged.","parameters":[{"schema":{"type":"string"},"required":true,"name":"tool_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolTestRequest"}}}},"responses":{"200":{"description":"What your server answered, or why it did not","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolTestResult"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown tool id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The tool's stored secret cannot be opened: `TOOL_SECRET_KEY` changed since it was saved. Rotate its secret, and save its headers again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"This deployment cannot seal a tool's secret (`TOOL_SECRET_KEY` unset): webhook tools cannot be saved or called.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agents/{agent_id}/tools":{"get":{"operationId":"listAgentTools","tags":["agents"],"summary":"The tools an agent can call","description":"The tools turned on for this agent, by name, in one page: at most the platform's `limits.agent.tools` (`GET /v1/platform/limits`).\n\n**Tools belong to the organisation, not to this agent**: the same one can be on any number of agents, and it is written on `/v1/tools`.","parameters":[{"schema":{"type":"string"},"required":true,"name":"agent_id","in":"path"}],"responses":{"200":{"description":"The tools this agent can call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"operationId":"setAgentTools","tags":["agents"],"summary":"Set which tools an agent can call","description":"Needs `agent:write`.\n\nReplaces this agent's tools with exactly the ones named, at most the platform's `limits.agent.tools` (`GET /v1/platform/limits`). **Declarative**: the list is the complete set, and an empty one turns every tool off. Ids that are not yours come back in `skipped`. Conversations already open pick the change up within about a minute.\n\n**Every tool rides on every model call**: each costs ~100–250 input tokens a call, whether it is used or not.","parameters":[{"schema":{"type":"string"},"required":true,"name":"agent_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentToolsUpdate"}}}},"responses":{"200":{"description":"How many tools were turned on and off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolsAssigned"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown agent id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/tts":{"post":{"operationId":"synthesizeSpeech","tags":["tts"],"summary":"Generate speech","description":"Synthesize speech from text with the chosen model and voice. Returns complete audio as WAV, or a chunked stream when `stream: true` (pcm only).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}}},"responses":{"200":{"description":"The generated audio — a complete WAV, or a chunked `audio/pcm` stream when `stream` was set. Timing headers describe the generation","headers":{"X-Audio-Seconds":{"description":"Duration of the generated audio","schema":{"type":"string"}},"X-Generation-Seconds":{"description":"Wall time of the provider's call, the network included","schema":{"type":"string"}},"X-Model":{"description":"Model that served the request","schema":{"type":"string"}},"X-Request-Id":{"description":"Unique request id — include when reporting issues","schema":{"type":"string"}},"X-Sample-Rate":{"description":"Hz of the returned audio","schema":{"type":"string"}},"X-Stream-Chunking":{"description":"How a streamed response was split, when it was","schema":{"type":"string"}},"X-Total-Seconds":{"description":"Wall time inside the gateway","schema":{"type":"string"}}},"content":{"audio/wav":{"schema":{"type":"string"}},"audio/pcm":{"schema":{"type":"string","format":"binary","description":"Raw 16-bit signed little-endian mono at `sample_rate` (24 kHz unless asked otherwise), sent as it is generated — what `stream: true` returns, and **only** with `format: pcm`."}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown voice_id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The provider failed; the message says why","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Speech is not configured on this platform, or the voice could not be looked up — the message says which","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/stt":{"post":{"operationId":"transcribe","tags":["transcription"],"summary":"Transcribe audio","description":"Send audio and get the text back — as `multipart/form-data` with a `file` part, or as the raw body with the options as query parameters. Any format the provider reads is accepted. `stream=true` returns newline-delimited JSON (WAV or raw PCM only), one `TranscriptionStreamLine` per line. Use `POST /v1/transcriptions` for anything long. Charged per minute of audio, on `duration_seconds`.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"additionalProperties":false,"description":"A `file` part, with the options as form fields. An unknown field is refused; a boolean is `true` or `false`.","properties":{"file":{"type":"string","format":"binary","description":"The audio. Any container the provider reads."},"stream":{"type":"boolean","default":false,"description":"Return newline-delimited JSON instead of one body. Requires WAV or raw PCM."},"model":{"type":"string","enum":["scribe_v2"],"default":"scribe_v2"},"language":{"type":"string","minLength":2,"maxLength":8,"description":"The language spoken, as an ISO 639-1 or 639-3 code; unset detects it."},"diarize":{"type":"boolean","default":false},"num_speakers":{"type":"integer","minimum":1,"maximum":32},"tag_audio_events":{"type":"boolean","default":false},"timestamps":{"type":"boolean","default":false,"description":"Return each word and its timing as `words`."}}}},"application/octet-stream":{"schema":{"type":"string","format":"binary","description":"The audio itself, with the options as query parameters."}}}},"responses":{"200":{"description":"The transcript","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcription"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/TranscriptionStreamLine"}}}},"400":{"description":"The multipart body could not be read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"The file is larger than 25 MB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"That content type is not audio, or `stream=true` was asked of audio it cannot cut","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The provider failed; the message says why","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Transcription is not configured on this platform","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/voices":{"get":{"operationId":"listVoices","tags":["voices"],"summary":"List available voices","description":"Every voice this API key may use: the built-in voice, any voices the platform provides, and any your organisation has uploaded. `id` is what `POST /v1/tts` takes; `name` is a label and is not unique. Only a `ready` voice speaks. One page: `next_cursor` is always `null`.","responses":{"200":{"description":"Every voice available to this key, built-in first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceInfoPage"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The voices could not be looked up; the message names the built-in voice, which needs no lookup","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createVoice","tags":["voices"],"summary":"Upload a voice","description":"Needs `voices:write`. Counts against your rate limit like a generation.\n\nCreates the voice from a recording uploaded to the URL from `POST /v1/voices/upload-url`: its `upload_id`, a `name`, an optional `description`, and `consent=true` confirming you have the right to clone this voice — the upload is refused without it.\n\nThe voice comes back with `status: pending` and **cannot be used for speech yet** — poll `GET /v1/voices/{voice_id}` until it is `ready`, or `failed` with the reason in `error`. The recording is cloned by ElevenLabs (instant voice cloning) after this answers, so a recording they refuse arrives as a `failed` status rather than as a 422.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceUpload"}}}},"responses":{"202":{"description":"Accepted. The voice is `pending` until it is cloned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"No room for another voice (as for the upload URL), the upload never arrived, or it is a voice already","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Object storage, or the worker, is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/voices/upload-url":{"post":{"operationId":"createVoiceUploadUrl","tags":["voices"],"summary":"Ask where to upload a voice's recording","description":"Needs `voices:write`. Counts against your rate limit.\n\nA short-lived URL to `PUT` the recording to, with its type signed in — send exactly that `Content-Type` and no authentication headers — and the `upload_id` to create the voice with afterwards (`POST /v1/voices`). At most the platform's `limits.uploads.voice_bytes` (`GET /v1/platform/limits`). Nothing is created until then.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceUploadRequest"}}}},"responses":{"200":{"description":"Where to PUT the recording, and the upload to create the voice from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceUploadTicket"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The organisation already has as many voices as its plan allows — never more than the platform's `limits.org.voices` (`GET /v1/platform/limits`) — or the provider's account has no room for another. Delete one, or ask for a higher limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Object storage is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/voices/design":{"post":{"operationId":"designVoice","tags":["voices"],"summary":"Design a voice from a description","description":"Needs `voices:write`.\n\nDescribes a voice in words and gets back several candidates to listen to, as base64 audio. **Nothing is saved** — keep one with `POST /v1/voices/design/save`.\n\nBilled per design, whether or not a candidate is kept.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDesignRequest"}}}},"responses":{"200":{"description":"Candidates to listen to, and the brief they were designed from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDesignResult"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The designer failed; the message says why","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Voice design or object storage not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/voices/design/save":{"post":{"operationId":"saveDesignedVoice","tags":["voices"],"summary":"Keep a designed voice","description":"Needs `voices:write`.\n\nTurns one of the candidates from `POST /v1/voices/design` into a voice: send back its `id` as `preview_id`, a `name`, and the `prompt`. Not billed — the design was. The voice is `ready` at once; its spoken samples follow.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDesignSaveRequest"}}}},"responses":{"202":{"description":"Accepted. The voice is ready; its samples follow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The organisation already has as many voices as its plan allows — never more than the platform's `limits.org.voices` (`GET /v1/platform/limits`) — or the provider's account has no room for another. Delete one, or ask for a higher limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The provider could not keep the preview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Object storage, or the worker, is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/voices/{voice_id}":{"get":{"operationId":"getVoice","tags":["voices"],"summary":"Read a voice","description":"One of your organisation's own voices, with its `status` — `pending` while the upload is being cloned, `failed` with the reason in `error`, `ready` otherwise. Only voices your organisation owns.","parameters":[{"schema":{"type":"string"},"required":true,"name":"voice_id","in":"path"}],"responses":{"200":{"description":"The voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateVoice","tags":["voices"],"summary":"Rename or re-describe a voice","description":"Needs `voices:write`.\n\nA partial update: fields you omit are left alone. **There is no way to replace the recording** — upload a new voice and delete the old one.","parameters":[{"schema":{"type":"string"},"required":true,"name":"voice_id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceUpdate"}}}},"responses":{"200":{"description":"The voice as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteVoice","tags":["voices"],"summary":"Delete a voice","description":"Needs `voices:write`.\n\nRemoves the voice, its samples, and the voice we made for it at the provider. A `POST /v1/tts` naming the id afterwards is a 404.","parameters":[{"schema":{"type":"string"},"required":true,"name":"voice_id","in":"path"}],"responses":{"204":{"description":"Deleted"},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/voices/{voice_id}/sample":{"post":{"operationId":"resampleVoice","tags":["voices"],"summary":"Retry a voice, or regenerate its samples","description":"Needs `voices:write`. Counts against your rate limit like a generation.\n\nA `failed` voice has its clone run again; a `ready` one has its spoken samples regenerated; a `pending` one is already queued and answers 409.","parameters":[{"schema":{"type":"string"},"required":true,"name":"voice_id","in":"path"}],"responses":{"200":{"description":"The voice, `pending` again where a clone was requeued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDetail"}}}},"400":{"description":"The request could not be read: malformed JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits, or the organisation is suspended — nothing was charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Still being processed — nothing to retry yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Object storage, or the worker, is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"webhooks":{},"servers":[{"url":"https://api.lumavox.ai"}]}