diff --git a/agents/test/agent-tests.mdx b/agents/test/agent-tests.mdx index 145b377..474ac8d 100644 --- a/agents/test/agent-tests.mdx +++ b/agents/test/agent-tests.mdx @@ -169,6 +169,52 @@ Tests always exercise the agent's latest **draft** configuration, including unpu +## Run tests from the API and CI + +Everything on this page is also available over the REST API with an [API key](/developer-guide/getting-started/api-key), so you can keep tests next to your agent configuration and gate releases on them. See the [Tests API reference](/api-reference/endpoint/agent/list-tests) for every endpoint. + +- **Export and import.** [Get Test](/api-reference/endpoint/agent/get-test) returns a test's full definition. Post that body to [Create Test](/api-reference/endpoint/agent/create-test) as is to recreate it, for example from JSON files kept in your repository. Read-only fields such as `test_id` and `created_at` are ignored. Tool references use your workspace's tool ids, so an export imports as is within the same team. [List Test Tools](/api-reference/endpoint/agent/list-test-tools) returns the ids a test can reference for an agent, including integration tools. +- **Attach.** Pass `agent_ids` when you create a test, or attach it later with [Attach Test](/api-reference/endpoint/agent/attach-test). +- **Run and wait.** [Run Tests](/api-reference/endpoint/agent/run-tests) starts a batch with every attached test, or only the `test_ids` you pass, and `repeat_count` overrides each test's repeat count for that batch. Poll [Get Test Batch](/api-reference/endpoint/agent/get-test-batch) until `completed` is `true`, then check `passed`, `failed`, `errors`, and `pass_rate`. `pass_rate` leaves out runs that ended in **Error**, so decide in your pipeline whether an error should fail the build or be retried. +- **History.** [List Test Batches](/api-reference/endpoint/agent/list-test-batches) and [List Test Runs](/api-reference/endpoint/agent/list-test-runs) return past results, filterable by test, batch, and status. + +Runs always use the agent's current draft. A pipeline that changes the agent should update the draft first ([Configure through the API](/agents/build/configuration#configure-through-the-api)), run the tests, and publish only when they pass. + +This GitHub Actions workflow runs every test attached to an agent and fails the build if any run fails or errors: + +```yaml .github/workflows/agent-tests.yml +name: Agent tests +on: [pull_request] + +jobs: + agent-tests: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Run the agent's tests + env: + FISH_API_KEY: ${{ secrets.FISH_API_KEY }} + AGENT_ID: ${{ vars.AGENT_ID }} + run: | + set -euo pipefail + api=https://api.fish.audio/v1/agent + auth="Authorization: Bearer $FISH_API_KEY" + + batch=$(curl -sf -X POST "$api/agents/$AGENT_ID/tests/run" \ + -H "$auth" -H "Content-Type: application/json" -d '{}' | jq -r .batch_id) + + while true; do + result=$(curl -sf "$api/agents/$AGENT_ID/test-batches/$batch" -H "$auth") + [ "$(jq -r .completed <<<"$result")" = "true" ] && break + sleep 15 + done + + jq '{passed, failed, errors, pass_rate}' <<<"$result" + jq -e '.failed == 0 and .errors == 0' <<<"$result" > /dev/null +``` + +Simulation tests take minutes, so give the job a timeout that covers your longest scenarios. To tolerate occasional failures of repeated Simulation tests, gate on `pass_rate` instead, for example `jq -e '.pass_rate >= 0.8'`. + ## Limits | Field | Limit | @@ -182,6 +228,7 @@ Tests always exercise the agent's latest **draft** configuration, including unpu | Success conditions (Simulation) | 1 to 10, description 500 characters each | | Max turns (Simulation) | 50 | | Repeat count (Simulation) | 20 | +| Runs per API batch | 1,000 (tests times repeats) | | Dynamic variables | 50 per test | ## Going further diff --git a/api-reference/endpoint/agent/attach-test.mdx b/api-reference/endpoint/agent/attach-test.mdx new file mode 100644 index 0000000..7651787 --- /dev/null +++ b/api-reference/endpoint/agent/attach-test.mdx @@ -0,0 +1,7 @@ +--- +openapi: put /v1/agent/agents/{agent_id}/tests/{test_id} +title: "Attach Test" +description: "Attach a test to an agent so the agent's test runs include it." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/create-test.mdx b/api-reference/endpoint/agent/create-test.mdx new file mode 100644 index 0000000..1a9635b --- /dev/null +++ b/api-reference/endpoint/agent/create-test.mdx @@ -0,0 +1,7 @@ +--- +openapi: post /v1/agent/tests +title: "Create Test" +description: "Create a Single Turn, Tool or Simulation test, optionally attached to agents." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/delete-test.mdx b/api-reference/endpoint/agent/delete-test.mdx new file mode 100644 index 0000000..5e4a8b9 --- /dev/null +++ b/api-reference/endpoint/agent/delete-test.mdx @@ -0,0 +1,7 @@ +--- +openapi: delete /v1/agent/tests/{test_id} +title: "Delete Test" +description: "Delete a test and detach it from every agent." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/detach-test.mdx b/api-reference/endpoint/agent/detach-test.mdx new file mode 100644 index 0000000..33f3520 --- /dev/null +++ b/api-reference/endpoint/agent/detach-test.mdx @@ -0,0 +1,7 @@ +--- +openapi: delete /v1/agent/agents/{agent_id}/tests/{test_id} +title: "Detach Test" +description: "Detach a test from an agent. The test stays in your library." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/get-test-batch.mdx b/api-reference/endpoint/agent/get-test-batch.mdx new file mode 100644 index 0000000..c27725e --- /dev/null +++ b/api-reference/endpoint/agent/get-test-batch.mdx @@ -0,0 +1,7 @@ +--- +openapi: get /v1/agent/agents/{agent_id}/test-batches/{batch_id} +title: "Get Test Batch" +description: "The runs of one batch with their pass, fail and error counts." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/get-test-run.mdx b/api-reference/endpoint/agent/get-test-run.mdx new file mode 100644 index 0000000..b46f820 --- /dev/null +++ b/api-reference/endpoint/agent/get-test-run.mdx @@ -0,0 +1,7 @@ +--- +openapi: get /v1/agent/test-runs/{run_id} +title: "Get Test Run" +description: "Fetch one test run with its transcript, verdicts and tool calls." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/get-test.mdx b/api-reference/endpoint/agent/get-test.mdx new file mode 100644 index 0000000..2c3e904 --- /dev/null +++ b/api-reference/endpoint/agent/get-test.mdx @@ -0,0 +1,7 @@ +--- +openapi: get /v1/agent/tests/{test_id} +title: "Get Test" +description: "Fetch one test's full definition, which is also the export format." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/list-test-batches.mdx b/api-reference/endpoint/agent/list-test-batches.mdx new file mode 100644 index 0000000..556b743 --- /dev/null +++ b/api-reference/endpoint/agent/list-test-batches.mdx @@ -0,0 +1,7 @@ +--- +openapi: get /v1/agent/agents/{agent_id}/test-batches +title: "List Test Batches" +description: "The agent's test batches, newest first, with their counts." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/list-test-runs.mdx b/api-reference/endpoint/agent/list-test-runs.mdx new file mode 100644 index 0000000..64ac750 --- /dev/null +++ b/api-reference/endpoint/agent/list-test-runs.mdx @@ -0,0 +1,7 @@ +--- +openapi: get /v1/agent/test-runs +title: "List Test Runs" +description: "List your team's test runs, newest first." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/list-test-tools.mdx b/api-reference/endpoint/agent/list-test-tools.mdx new file mode 100644 index 0000000..c02f60a --- /dev/null +++ b/api-reference/endpoint/agent/list-test-tools.mdx @@ -0,0 +1,7 @@ +--- +openapi: get /v1/agent/agents/{agent_id}/test-tools +title: "List Test Tools" +description: "The tool ids a test can reference for this agent." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/list-tests.mdx b/api-reference/endpoint/agent/list-tests.mdx new file mode 100644 index 0000000..c1bc161 --- /dev/null +++ b/api-reference/endpoint/agent/list-tests.mdx @@ -0,0 +1,7 @@ +--- +openapi: get /v1/agent/tests +title: "List Tests" +description: "List your team's tests, newest first." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/run-tests.mdx b/api-reference/endpoint/agent/run-tests.mdx new file mode 100644 index 0000000..c61e98f --- /dev/null +++ b/api-reference/endpoint/agent/run-tests.mdx @@ -0,0 +1,7 @@ +--- +openapi: post /v1/agent/agents/{agent_id}/tests/run +title: "Run Tests" +description: "Start a batch that runs the agent's attached tests against its draft." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/endpoint/agent/update-test.mdx b/api-reference/endpoint/agent/update-test.mdx new file mode 100644 index 0000000..4abd50d --- /dev/null +++ b/api-reference/endpoint/agent/update-test.mdx @@ -0,0 +1,7 @@ +--- +openapi: patch /v1/agent/tests/{test_id} +title: "Update Test" +description: "Patch test fields. Omitted fields keep their value." +icon: "flask" +iconType: "solid" +--- diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 753f385..8114d59 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -1562,6 +1562,42 @@ "default": null, "title": "Duration Seconds" }, + "participant_identity": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Participant Identity" + }, + "agent_participant_identity": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Participant Identity" + }, + "agent_joined_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Joined At" + }, "metadata": { "additionalProperties": { "$ref": "#/components/schemas/JsonValue" @@ -4887,10 +4923,10 @@ ] } }, - "/v1/agent/available-phone-numbers": { - "get": { - "summary": "Search Available Phone Numbers", - "description": "Search the purchasable number inventory. Buy an entry with\n`POST /v1/agent/phone-numbers`; availability is not a reservation, so a listed\nnumber can still be claimed by someone else first.", + "/v1/agent/agents/{agent_id}/tests/{test_id}": { + "put": { + "summary": "Attach Test", + "description": "Attach a test to an agent so the agent's test runs include it. Attaching\nan attached test does nothing. The agent and the test must be in the same\nworkspace.", "security": [ { "BearerAuth": [] @@ -4898,98 +4934,32 @@ ], "parameters": [ { - "in": "query", - "name": "country_code", - "description": "ISO 3166-1 alpha-2 country code; `US` or `CA`.", - "required": false, + "in": "path", + "name": "agent_id", + "description": "", + "required": true, "schema": { - "default": "US", - "title": "Country Code", + "title": "Agent Id", "type": "string" }, "deprecated": false }, { - "in": "query", - "name": "area_code", - "description": "Restrict to one area code, e.g. 415.", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Area Code" - }, - "deprecated": false - }, - { - "in": "query", - "name": "number_type", - "description": "The managed inventory is US/CA local numbers only; `toll_free` currently returns 400.", - "required": false, + "in": "path", + "name": "test_id", + "description": "", + "required": true, "schema": { - "default": "local", - "enum": [ - "local", - "toll_free" - ], - "title": "Number Type", + "title": "Test Id", "type": "string" }, "deprecated": false - }, - { - "in": "query", - "name": "provider", - "description": "Inventory to search. Only the managed `twilio` inventory (call-transfer support) is available; the parameter is kept so importing your own numbers can extend it later.", - "required": false, - "schema": { - "anyOf": [ - { - "const": "twilio", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Provider" - }, - "deprecated": false } ], "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "available_phone_numbers": { - "description": "Inventory entries forwarded from the provider (number, region, pricing).", - "items": { - "additionalProperties": true, - "type": "object" - }, - "title": "Available Phone Numbers", - "type": "array" - } - }, - "required": [ - "available_phone_numbers" - ], - "type": "object" - } - } - } + "204": { + "description": "Request fulfilled, nothing follows", + "headers": {} }, "400": { "description": "Bad request syntax or unsupported method", @@ -5065,8 +5035,8 @@ } } }, - "502": { - "description": "Invalid responses from another server/proxy", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -5141,14 +5111,12 @@ } }, "tags": [ - "Phone Numbers" + "Agent Tests" ] - } - }, - "/v1/agent/knowledge-sources": { - "get": { - "summary": "List Knowledge Sources", - "description": "List your team's knowledge sources, newest first. Paginate with `cursor`\n(follow `next_cursor` while `has_more` is true) or with `page` for offset\npagination with a `total` count — the two are mutually exclusive.", + }, + "delete": { + "summary": "Detach Test", + "description": "Detach a test from an agent. The test itself stays in your library.\nDetaching a test that is not attached does nothing.", "security": [ { "BearerAuth": [] @@ -5156,110 +5124,49 @@ ], "parameters": [ { - "in": "query", - "name": "search", - "description": "Case-insensitive match on name and file name.", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Search" - }, - "deprecated": false - }, - { - "in": "query", - "name": "cursor", - "description": "", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Cursor" - }, - "deprecated": false - }, - { - "in": "query", - "name": "page", - "description": "1-based page number; mutually exclusive with cursor.", - "required": false, - "schema": { - "anyOf": [ - { - "minimum": 1, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Page" - }, - "deprecated": false - }, - { - "in": "query", - "name": "include_total", + "in": "path", + "name": "agent_id", "description": "", - "required": false, + "required": true, "schema": { - "default": false, - "title": "Include Total", - "type": "boolean" + "title": "Agent Id", + "type": "string" }, "deprecated": false }, { - "in": "query", - "name": "page_size", + "in": "path", + "name": "test_id", "description": "", - "required": false, + "required": true, "schema": { - "default": 30, - "maximum": 100, - "minimum": 1, - "title": "Page Size", - "type": "integer" + "title": "Test Id", + "type": "string" }, "deprecated": false } ], "responses": { - "200": { - "description": "Request fulfilled, document follows", + "204": { + "description": "Request fulfilled, nothing follows", + "headers": {} + }, + "400": { + "description": "Bad request syntax or unsupported method", "headers": {}, "content": { "application/json": { "schema": { "properties": { - "knowledge_sources": { - "items": { - "$ref": "#/components/schemas/PublicKnowledgeSourceSummary" - }, - "title": "Knowledge Sources", - "type": "array" + "status": { + "title": "Status", + "type": "integer" }, - "has_more": { - "title": "Has More", - "type": "boolean" + "message": { + "title": "Message", + "type": "string" }, - "next_cursor": { + "reason": { "anyOf": [ { "type": "string" @@ -5269,32 +5176,20 @@ } ], "default": null, - "title": "Next Cursor" - }, - "total": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Total" + "title": "Reason" } }, "required": [ - "knowledge_sources", - "has_more" + "status", + "message" ], "type": "object" } } } }, - "400": { - "description": "Bad request syntax or unsupported method", + "401": { + "description": "No permission -- see authorization schemes", "headers": {}, "content": { "application/json": { @@ -5330,8 +5225,8 @@ } } }, - "401": { - "description": "No permission -- see authorization schemes", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -5406,123 +5301,103 @@ } }, "tags": [ - "Knowledge Sources" + "Agent Tests" ] - }, + } + }, + "/v1/agent/agents/{agent_id}/tests/run": { "post": { - "summary": "Create Knowledge Source", - "description": "Upload a plain-text or Markdown file (UTF-8, up to 1 MB) as a knowledge\nsource. Send `multipart/form-data` with the file in `source`; `name`\ndefaults to the file name. Attach it to agents via the agent config's\n`knowledge_base.knowledge_source_ids`.", + "summary": "Run Tests", + "description": "Start a batch that runs the agent's attached tests against its current\ndraft, one run per test and repeat. A simulation test repeats\n`simulation.repeat_count` times, and a `repeat_count` in the body repeats\nevery selected test that many times instead. A batch starts at most 1000\nruns. Runs are queued and finish in the background, simulations take\nminutes. Poll `GET /v1/agent/agents/{agent_id}/test-batches/{batch_id}`\nuntil `completed` is true, then gate on `pass_rate`, which leaves out\n`error` runs.", "security": [ { "BearerAuth": [] } ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "properties": { - "source": { - "format": "binary", - "title": "Source", - "type": "string" - }, - "name": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Name" - }, - "description": { - "default": "", - "title": "Description", - "type": "string" - } - }, - "required": [ - "source" - ], - "type": "object" - } - } + "parameters": [ + { + "in": "path", + "name": "agent_id", + "description": "", + "required": true, + "schema": { + "title": "Agent Id", + "type": "string" + }, + "deprecated": false } - }, + ], "responses": { - "201": { - "description": "Document created, URL follows", + "202": { + "description": "Request accepted, processing continues off-line", "headers": {}, "content": { "application/json": { "schema": { "properties": { - "knowledge_source_id": { - "title": "Knowledge Source Id", - "type": "string" - }, - "workspace_id": { - "title": "Workspace Id", + "batch_id": { + "title": "Batch Id", "type": "string" }, - "name": { - "title": "Name", + "agent_id": { + "title": "Agent Id", "type": "string" }, - "description": { - "default": "", - "title": "Description", + "status": { + "description": "running until every run has finished.", + "enum": [ + "running", + "completed" + ], + "title": "Status", "type": "string" }, - "file_name": { - "title": "File Name", - "type": "string" + "completed": { + "title": "Completed", + "type": "boolean" }, - "size_bytes": { - "title": "Size Bytes", + "passed": { + "title": "Passed", "type": "integer" }, - "chunk_count": { - "title": "Chunk Count", + "failed": { + "title": "Failed", "type": "integer" }, - "revision_number": { - "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", - "title": "Revision Number", + "errors": { + "title": "Errors", "type": "integer" }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" + "pass_rate": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "passed / (passed + failed), null until a run passed or failed. Error runs never judged the agent and are left out.", + "title": "Pass Rate" }, - "content": { - "description": "The full current text content.", - "title": "Content", - "type": "string" + "runs": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestRun" + }, + "title": "Runs", + "type": "array" } }, "required": [ - "knowledge_source_id", - "workspace_id", - "name", - "file_name", - "size_bytes", - "chunk_count", - "revision_number", - "created_at", - "updated_at", - "content" + "batch_id", + "agent_id", + "status", + "completed", + "passed", + "failed", + "errors", + "runs" ], "type": "object" } @@ -5603,8 +5478,8 @@ } } }, - "403": { - "description": "Request forbidden -- authorization will not help", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -5716,14 +5591,48 @@ } }, "tags": [ - "Knowledge Sources" - ] + "Agent Tests" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Run Tests", + "source": "curl --request POST \\\n --url https://api.fish.audio/v1/agent/agents//tests/run \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --data '{}'" + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false, + "properties": { + "test_ids": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "description": "Attached tests to run. Omit to run every test attached to the agent." + }, + "repeat_count": { + "type": "integer", + "minimum": 1, + "maximum": 20, + "description": "Run every selected test this many times in this batch, in place of each test's own repeat count." + } + } + } + } + } + } } }, - "/v1/agent/knowledge-sources/{source_id}": { + "/v1/agent/agents/{agent_id}/test-tools": { "get": { - "summary": "Get Knowledge Source", - "description": "Fetch one knowledge source including its full text content and current\nrevision number.", + "summary": "List Test Tools", + "description": "The tools a test can reference for this agent, exactly what a\nconversation on its draft offers. Use these ids in a test's tool mocks,\nassertions and `referenced_tool`, including integration tools such as\n`google_calendar:create_event`.", "security": [ { "BearerAuth": [] @@ -5732,11 +5641,11 @@ "parameters": [ { "in": "path", - "name": "source_id", + "name": "agent_id", "description": "", "required": true, "schema": { - "title": "Source Id", + "title": "Agent Id", "type": "string" }, "deprecated": false @@ -5750,67 +5659,16 @@ "application/json": { "schema": { "properties": { - "knowledge_source_id": { - "title": "Knowledge Source Id", - "type": "string" - }, - "workspace_id": { - "title": "Workspace Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "file_name": { - "title": "File Name", - "type": "string" - }, - "size_bytes": { - "title": "Size Bytes", - "type": "integer" - }, - "chunk_count": { - "title": "Chunk Count", - "type": "integer" - }, - "revision_number": { - "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", - "title": "Revision Number", - "type": "integer" - }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" - }, - "content": { - "description": "The full current text content.", - "title": "Content", - "type": "string" + "tools": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestTool" + }, + "title": "Tools", + "type": "array" } }, "required": [ - "knowledge_source_id", - "workspace_id", - "name", - "file_name", - "size_bytes", - "chunk_count", - "revision_number", - "created_at", - "updated_at", - "content" + "tools" ], "type": "object" } @@ -5930,12 +5788,14 @@ } }, "tags": [ - "Knowledge Sources" + "Agent Tests" ] - }, - "patch": { - "summary": "Update Knowledge Source", - "description": "Rename the source and/or replace its content by uploading a new file in\n`source` (multipart). Replacing content increments `revision_number` while\nthe id stays stable, and every attached agent's draft picks it up\nimmediately; published versions pin the revision they were published with,\nso republish each affected agent to put the new content live — that\ntwo-step (sync content, republish) is the nightly content-sync recipe.", + } + }, + "/v1/agent/agents/{agent_id}/test-batches": { + "get": { + "summary": "List Test Batches", + "description": "The agent's test batches, newest first, with their pass, fail and error\ncounts. Paginate with `cursor`, following `next_cursor` while `has_more` is\ntrue.", "security": [ { "BearerAuth": [] @@ -5944,65 +5804,49 @@ "parameters": [ { "in": "path", - "name": "source_id", + "name": "agent_id", "description": "", "required": true, "schema": { - "title": "Source Id", + "title": "Agent Id", "type": "string" }, "deprecated": false - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "properties": { - "source": { - "anyOf": [ - { - "format": "binary", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Source" - }, - "name": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Name" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - } + }, + { + "in": "query", + "name": "cursor", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" }, - "type": "object" - } - } + { + "type": "null" + } + ], + "default": null, + "title": "Cursor" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page_size", + "description": "", + "required": false, + "schema": { + "default": 30, + "maximum": 100, + "minimum": 1, + "title": "Page Size", + "type": "integer" + }, + "deprecated": false } - }, + ], "responses": { "200": { "description": "Request fulfilled, document follows", @@ -6011,89 +5855,18 @@ "application/json": { "schema": { "properties": { - "knowledge_source_id": { - "title": "Knowledge Source Id", - "type": "string" - }, - "workspace_id": { - "title": "Workspace Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "file_name": { - "title": "File Name", - "type": "string" - }, - "size_bytes": { - "title": "Size Bytes", - "type": "integer" - }, - "chunk_count": { - "title": "Chunk Count", - "type": "integer" - }, - "revision_number": { - "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", - "title": "Revision Number", - "type": "integer" - }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" - }, - "content": { - "description": "The full current text content.", - "title": "Content", - "type": "string" - } - }, - "required": [ - "knowledge_source_id", - "workspace_id", - "name", - "file_name", - "size_bytes", - "chunk_count", - "revision_number", - "created_at", - "updated_at", - "content" - ], - "type": "object" - } - } - } - }, - "400": { - "description": "Bad request syntax or unsupported method", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" + "batches": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestBatchSummary" + }, + "title": "Batches", + "type": "array" }, - "message": { - "title": "Message", - "type": "string" + "has_more": { + "title": "Has More", + "type": "boolean" }, - "reason": { + "next_cursor": { "anyOf": [ { "type": "string" @@ -6103,20 +5876,20 @@ } ], "default": null, - "title": "Reason" + "title": "Next Cursor" } }, "required": [ - "status", - "message" + "batches", + "has_more" ], "type": "object" } } } }, - "401": { - "description": "No permission -- see authorization schemes", + "400": { + "description": "Bad request syntax or unsupported method", "headers": {}, "content": { "application/json": { @@ -6152,8 +5925,8 @@ } } }, - "404": { - "description": "Nothing matches the given URI", + "401": { + "description": "No permission -- see authorization schemes", "headers": {}, "content": { "application/json": { @@ -6189,8 +5962,8 @@ } } }, - "422": { - "description": "", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -6265,12 +6038,14 @@ } }, "tags": [ - "Knowledge Sources" + "Agent Tests" ] - }, - "delete": { - "summary": "Delete Knowledge Source", - "description": "Delete a knowledge source. Returns 409 while any agent still references it\n— in its draft or in its currently published version (sessions resolve\nsources at call time, so deleting a published reference would change a\nrunning agent). Check `GET /v1/agent/knowledge-sources/{source_id}/agents`,\ndetach via the agent config, and republish if needed before deleting.", + } + }, + "/v1/agent/agents/{agent_id}/test-batches/{batch_id}": { + "get": { + "summary": "Get Test Batch", + "description": "The runs of one batch with their pass, fail and error counts. `status` is\n`running` until every run has finished, then `completed`.", "security": [ { "BearerAuth": [] @@ -6279,60 +6054,106 @@ "parameters": [ { "in": "path", - "name": "source_id", + "name": "agent_id", "description": "", "required": true, "schema": { - "title": "Source Id", + "title": "Agent Id", + "type": "string" + }, + "deprecated": false + }, + { + "in": "path", + "name": "batch_id", + "description": "", + "required": true, + "schema": { + "title": "Batch Id", "type": "string" }, "deprecated": false } ], "responses": { - "204": { - "description": "Request fulfilled, nothing follows", - "headers": {} - }, - "401": { - "description": "No permission -- see authorization schemes", + "200": { + "description": "Request fulfilled, document follows", "headers": {}, "content": { "application/json": { "schema": { "properties": { + "batch_id": { + "title": "Batch Id", + "type": "string" + }, + "agent_id": { + "title": "Agent Id", + "type": "string" + }, "status": { + "description": "running until every run has finished.", + "enum": [ + "running", + "completed" + ], "title": "Status", + "type": "string" + }, + "completed": { + "title": "Completed", + "type": "boolean" + }, + "passed": { + "title": "Passed", "type": "integer" }, - "message": { - "title": "Message", - "type": "string" + "failed": { + "title": "Failed", + "type": "integer" }, - "reason": { + "errors": { + "title": "Errors", + "type": "integer" + }, + "pass_rate": { "anyOf": [ { - "type": "string" + "type": "number" }, { "type": "null" } ], "default": null, - "title": "Reason" + "description": "passed / (passed + failed), null until a run passed or failed. Error runs never judged the agent and are left out.", + "title": "Pass Rate" + }, + "runs": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestRun" + }, + "title": "Runs", + "type": "array" } }, "required": [ + "batch_id", + "agent_id", "status", - "message" + "completed", + "passed", + "failed", + "errors", + "runs" ], "type": "object" } } } }, - "404": { - "description": "Nothing matches the given URI", + "401": { + "description": "No permission -- see authorization schemes", "headers": {}, "content": { "application/json": { @@ -6368,8 +6189,8 @@ } } }, - "409": { - "description": "Request conflict", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -6444,14 +6265,21 @@ } }, "tags": [ - "Knowledge Sources" + "Agent Tests" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Poll Test Batch", + "source": "curl --request GET \\\n --url https://api.fish.audio/v1/agent/agents//test-batches/ \\\n --header 'Authorization: Bearer '" + } ] } }, - "/v1/agent/knowledge-sources/{source_id}/agents": { + "/v1/agent/available-phone-numbers": { "get": { - "summary": "List Agents Using Knowledge Source", - "description": "Every agent that references this source in its draft or currently\npublished configuration — the pre-flight check before a delete.", + "summary": "Search Available Phone Numbers", + "description": "Search the purchasable number inventory. Buy an entry with\n`POST /v1/agent/phone-numbers`; availability is not a reservation, so a listed\nnumber can still be claimed by someone else first.", "security": [ { "BearerAuth": [] @@ -6459,15 +6287,71 @@ ], "parameters": [ { - "in": "path", - "name": "source_id", - "description": "", - "required": true, - "schema": { - "title": "Source Id", + "in": "query", + "name": "country_code", + "description": "ISO 3166-1 alpha-2 country code; `US` or `CA`.", + "required": false, + "schema": { + "default": "US", + "title": "Country Code", + "type": "string" + }, + "deprecated": false + }, + { + "in": "query", + "name": "area_code", + "description": "Restrict to one area code, e.g. 415.", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Area Code" + }, + "deprecated": false + }, + { + "in": "query", + "name": "number_type", + "description": "The managed inventory is US/CA local numbers only; `toll_free` currently returns 400.", + "required": false, + "schema": { + "default": "local", + "enum": [ + "local", + "toll_free" + ], + "title": "Number Type", "type": "string" }, "deprecated": false + }, + { + "in": "query", + "name": "provider", + "description": "Inventory to search. Only the managed `twilio` inventory (call-transfer support) is available; the parameter is kept so importing your own numbers can extend it later.", + "required": false, + "schema": { + "anyOf": [ + { + "const": "twilio", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provider" + }, + "deprecated": false } ], "responses": { @@ -6478,22 +6362,55 @@ "application/json": { "schema": { "properties": { - "agents": { - "description": "Agents that reference this resource in their configuration.", + "available_phone_numbers": { + "description": "Inventory entries forwarded from the provider (number, region, pricing).", "items": { - "$ref": "#/components/schemas/PublicDependentAgent" + "additionalProperties": true, + "type": "object" }, - "title": "Agents", + "title": "Available Phone Numbers", "type": "array" - }, - "total": { - "title": "Total", + } + }, + "required": [ + "available_phone_numbers" + ], + "type": "object" + } + } + } + }, + "400": { + "description": "Bad request syntax or unsupported method", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" } }, "required": [ - "agents", - "total" + "status", + "message" ], "type": "object" } @@ -6537,8 +6454,8 @@ } } }, - "404": { - "description": "Nothing matches the given URI", + "502": { + "description": "Invalid responses from another server/proxy", "headers": {}, "content": { "application/json": { @@ -6613,14 +6530,14 @@ } }, "tags": [ - "Knowledge Sources" + "Phone Numbers" ] } }, - "/v1/agent/tools": { + "/v1/agent/knowledge-sources": { "get": { - "summary": "List Tools", - "description": "List your team's tools, newest first. Filter with `agent_id` to see one\nagent's attached tools. Paginate with `cursor` (follow `next_cursor` while\n`has_more` is true) or with `page` for offset pagination with a `total`\ncount — the two are mutually exclusive.", + "summary": "List Knowledge Sources", + "description": "List your team's knowledge sources, newest first. Paginate with `cursor`\n(follow `next_cursor` while `has_more` is true) or with `page` for offset\npagination with a `total` count — the two are mutually exclusive.", "security": [ { "BearerAuth": [] @@ -6630,7 +6547,7 @@ { "in": "query", "name": "search", - "description": "Case-insensitive match on name.", + "description": "Case-insensitive match on name and file name.", "required": false, "schema": { "anyOf": [ @@ -6646,25 +6563,6 @@ }, "deprecated": false }, - { - "in": "query", - "name": "agent_id", - "description": "Only tools attached to this agent's draft config.", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Agent Id" - }, - "deprecated": false - }, { "in": "query", "name": "cursor", @@ -6739,11 +6637,11 @@ "application/json": { "schema": { "properties": { - "tools": { + "knowledge_sources": { "items": { - "$ref": "#/components/schemas/PublicAgentToolSummary" + "$ref": "#/components/schemas/PublicKnowledgeSourceSummary" }, - "title": "Tools", + "title": "Knowledge Sources", "type": "array" }, "has_more": { @@ -6776,7 +6674,7 @@ } }, "required": [ - "tools", + "knowledge_sources", "has_more" ], "type": "object" @@ -6858,43 +6756,6 @@ } } }, - "404": { - "description": "Nothing matches the given URI", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - }, "503": { "description": "The server cannot process the request due to a high load", "headers": {}, @@ -6934,12 +6795,12 @@ } }, "tags": [ - "Agent Tools" + "Knowledge Sources" ] }, "post": { - "summary": "Create Tool", - "description": "Create a workspace tool. `tool_type: webhook` calls your HTTP endpoint\nduring conversations; `tool_type: client` is executed by your own frontend\nthrough the SDK (no URL). Attach it to agents via the agent config's\n`tools.tool_ids`. Credential headers (authorization_bearer /\nauthorization_basic) are write-only and read back as `has_secret`.", + "summary": "Create Knowledge Source", + "description": "Upload a plain-text or Markdown file (UTF-8, up to 1 MB) as a knowledge\nsource. Send `multipart/form-data` with the file in `source`; `name`\ndefaults to the file name. Attach it to agents via the agent config's\n`knowledge_base.knowledge_source_ids`.", "security": [ { "BearerAuth": [] @@ -6948,9 +6809,36 @@ "requestBody": { "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicAgentToolCreatePayload" + "properties": { + "source": { + "format": "binary", + "title": "Source", + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "description": { + "default": "", + "title": "Description", + "type": "string" + } + }, + "required": [ + "source" + ], + "type": "object" } } } @@ -6963,8 +6851,8 @@ "application/json": { "schema": { "properties": { - "tool_id": { - "title": "Tool Id", + "knowledge_source_id": { + "title": "Knowledge Source Id", "type": "string" }, "workspace_id": { @@ -6980,33 +6868,21 @@ "title": "Description", "type": "string" }, - "tool_type": { - "enum": [ - "webhook", - "client" - ], - "title": "Tool Type", + "file_name": { + "title": "File Name", "type": "string" }, - "method": { - "enum": [ - "GET", - "POST", - "PUT", - "PATCH", - "DELETE" - ], - "title": "Method", - "type": "string" + "size_bytes": { + "title": "Size Bytes", + "type": "integer" }, - "url": { - "title": "Url", - "type": "string" + "chunk_count": { + "title": "Chunk Count", + "type": "integer" }, - "used_by": { - "default": 0, - "description": "How many agents reference this tool in their draft configuration.", - "title": "Used By", + "revision_number": { + "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", + "title": "Revision Number", "type": "integer" }, "created_at": { @@ -7019,84 +6895,105 @@ "title": "Updated At", "type": "string" }, - "arguments": { - "items": { - "$ref": "#/components/schemas/AgentWebhookArgumentPayload" - }, - "title": "Arguments", - "type": "array" - }, - "content_type": { - "title": "Content Type", + "content": { + "description": "The full current text content.", + "title": "Content", "type": "string" + } + }, + "required": [ + "knowledge_source_id", + "workspace_id", + "name", + "file_name", + "size_bytes", + "chunk_count", + "revision_number", + "created_at", + "updated_at", + "content" + ], + "type": "object" + } + } + } + }, + "400": { + "description": "Bad request syntax or unsupported method", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "body_template": { - "default": "", - "title": "Body Template", + "message": { + "title": "Message", "type": "string" }, - "headers": { - "items": { - "$ref": "#/components/schemas/PublicToolHeader" - }, - "title": "Headers", - "type": "array" - }, - "timeout_seconds": { - "title": "Timeout Seconds", + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", "type": "integer" }, - "error_handling": { - "enum": [ - "passthrough", - "hide" - ], - "title": "Error Handling", + "message": { + "title": "Message", "type": "string" }, - "mock_responses": { - "items": { - "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" - }, - "title": "Mock Responses", - "type": "array" - }, - "expects_response": { - "default": true, - "title": "Expects Response", - "type": "boolean" - }, - "execution_mode": { - "default": "blocking", - "enum": [ - "blocking", - "fire_and_forget", - "background" + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } ], - "title": "Execution Mode", - "type": "string" + "default": null, + "title": "Reason" } }, "required": [ - "tool_id", - "workspace_id", - "name", - "tool_type", - "method", - "url", - "created_at", - "updated_at", - "content_type", - "timeout_seconds", - "error_handling" + "status", + "message" ], "type": "object" } } } }, - "401": { - "description": "No permission -- see authorization schemes", + "403": { + "description": "Request forbidden -- authorization will not help", "headers": {}, "content": { "application/json": { @@ -7208,21 +7105,14 @@ } }, "tags": [ - "Agent Tools" - ], - "x-codeSamples": [ - { - "lang": "bash", - "label": "Create Webhook Tool", - "source": "curl --request POST \\\n --url https://api.fish.audio/v1/agent/tools \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"name\": \"Book appointment\",\n \"description\": \"Creates an appointment in the scheduling system.\",\n \"method\": \"POST\",\n \"url\": \"https://api.example.com/appointments\",\n \"arguments\": [\n {\"name\": \"date\", \"description\": \"Appointment date, YYYY-MM-DD\"}\n ],\n \"body_template\": \"{\\\"date\\\": \\\"{{date}}\\\"}\",\n \"headers\": [\n {\"name\": \"Authorization\", \"value\": \"Bearer \", \"kind\": \"authorization_bearer\"}\n ]\n }'" - } + "Knowledge Sources" ] } }, - "/v1/agent/tools/{tool_id}": { + "/v1/agent/knowledge-sources/{source_id}": { "get": { - "summary": "Get Tool", - "description": "Fetch one tool's full definition. Credential header values are never\nreturned — each credential header reports `has_secret` instead.", + "summary": "Get Knowledge Source", + "description": "Fetch one knowledge source including its full text content and current\nrevision number.", "security": [ { "BearerAuth": [] @@ -7231,11 +7121,11 @@ "parameters": [ { "in": "path", - "name": "tool_id", + "name": "source_id", "description": "", "required": true, "schema": { - "title": "Tool Id", + "title": "Source Id", "type": "string" }, "deprecated": false @@ -7249,8 +7139,8 @@ "application/json": { "schema": { "properties": { - "tool_id": { - "title": "Tool Id", + "knowledge_source_id": { + "title": "Knowledge Source Id", "type": "string" }, "workspace_id": { @@ -7266,33 +7156,21 @@ "title": "Description", "type": "string" }, - "tool_type": { - "enum": [ - "webhook", - "client" - ], - "title": "Tool Type", + "file_name": { + "title": "File Name", "type": "string" }, - "method": { - "enum": [ - "GET", - "POST", - "PUT", - "PATCH", - "DELETE" - ], - "title": "Method", - "type": "string" + "size_bytes": { + "title": "Size Bytes", + "type": "integer" }, - "url": { - "title": "Url", - "type": "string" + "chunk_count": { + "title": "Chunk Count", + "type": "integer" }, - "used_by": { - "default": 0, - "description": "How many agents reference this tool in their draft configuration.", - "title": "Used By", + "revision_number": { + "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", + "title": "Revision Number", "type": "integer" }, "created_at": { @@ -7305,95 +7183,42 @@ "title": "Updated At", "type": "string" }, - "arguments": { - "items": { - "$ref": "#/components/schemas/AgentWebhookArgumentPayload" - }, - "title": "Arguments", - "type": "array" - }, - "content_type": { - "title": "Content Type", + "content": { + "description": "The full current text content.", + "title": "Content", "type": "string" + } + }, + "required": [ + "knowledge_source_id", + "workspace_id", + "name", + "file_name", + "size_bytes", + "chunk_count", + "revision_number", + "created_at", + "updated_at", + "content" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "body_template": { - "default": "", - "title": "Body Template", - "type": "string" - }, - "headers": { - "items": { - "$ref": "#/components/schemas/PublicToolHeader" - }, - "title": "Headers", - "type": "array" - }, - "timeout_seconds": { - "title": "Timeout Seconds", - "type": "integer" - }, - "error_handling": { - "enum": [ - "passthrough", - "hide" - ], - "title": "Error Handling", - "type": "string" - }, - "mock_responses": { - "items": { - "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" - }, - "title": "Mock Responses", - "type": "array" - }, - "expects_response": { - "default": true, - "title": "Expects Response", - "type": "boolean" - }, - "execution_mode": { - "default": "blocking", - "enum": [ - "blocking", - "fire_and_forget", - "background" - ], - "title": "Execution Mode", - "type": "string" - } - }, - "required": [ - "tool_id", - "workspace_id", - "name", - "tool_type", - "method", - "url", - "created_at", - "updated_at", - "content_type", - "timeout_seconds", - "error_handling" - ], - "type": "object" - } - } - } - }, - "401": { - "description": "No permission -- see authorization schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", + "message": { + "title": "Message", "type": "string" }, "reason": { @@ -7494,12 +7319,12 @@ } }, "tags": [ - "Agent Tools" + "Knowledge Sources" ] }, "patch": { - "summary": "Update Tool", - "description": "Patch tool fields; omitted fields keep their value (null is rejected —\nsend an empty string to clear a text field). `headers` replaces the header\nlist wholesale — include credential values again whenever you send it,\nsince reads never return them. Attached agents' drafts pick up the change\nimmediately; published versions stay frozen until re-published.", + "summary": "Update Knowledge Source", + "description": "Rename the source and/or replace its content by uploading a new file in\n`source` (multipart). Replacing content increments `revision_number` while\nthe id stays stable, and every attached agent's draft picks it up\nimmediately; published versions pin the revision they were published with,\nso republish each affected agent to put the new content live — that\ntwo-step (sync content, republish) is the nightly content-sync recipe.", "security": [ { "BearerAuth": [] @@ -7508,11 +7333,11 @@ "parameters": [ { "in": "path", - "name": "tool_id", + "name": "source_id", "description": "", "required": true, "schema": { - "title": "Tool Id", + "title": "Source Id", "type": "string" }, "deprecated": false @@ -7521,9 +7346,48 @@ "requestBody": { "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicAgentToolUpdatePayload" + "properties": { + "source": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + } + }, + "type": "object" } } } @@ -7536,8 +7400,8 @@ "application/json": { "schema": { "properties": { - "tool_id": { - "title": "Tool Id", + "knowledge_source_id": { + "title": "Knowledge Source Id", "type": "string" }, "workspace_id": { @@ -7553,33 +7417,21 @@ "title": "Description", "type": "string" }, - "tool_type": { - "enum": [ - "webhook", - "client" - ], - "title": "Tool Type", + "file_name": { + "title": "File Name", "type": "string" }, - "method": { - "enum": [ - "GET", - "POST", - "PUT", - "PATCH", - "DELETE" - ], - "title": "Method", - "type": "string" + "size_bytes": { + "title": "Size Bytes", + "type": "integer" }, - "url": { - "title": "Url", - "type": "string" + "chunk_count": { + "title": "Chunk Count", + "type": "integer" }, - "used_by": { - "default": 0, - "description": "How many agents reference this tool in their draft configuration.", - "title": "Used By", + "revision_number": { + "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", + "title": "Revision Number", "type": "integer" }, "created_at": { @@ -7592,76 +7444,23 @@ "title": "Updated At", "type": "string" }, - "arguments": { - "items": { - "$ref": "#/components/schemas/AgentWebhookArgumentPayload" - }, - "title": "Arguments", - "type": "array" - }, - "content_type": { - "title": "Content Type", - "type": "string" - }, - "body_template": { - "default": "", - "title": "Body Template", - "type": "string" - }, - "headers": { - "items": { - "$ref": "#/components/schemas/PublicToolHeader" - }, - "title": "Headers", - "type": "array" - }, - "timeout_seconds": { - "title": "Timeout Seconds", - "type": "integer" - }, - "error_handling": { - "enum": [ - "passthrough", - "hide" - ], - "title": "Error Handling", - "type": "string" - }, - "mock_responses": { - "items": { - "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" - }, - "title": "Mock Responses", - "type": "array" - }, - "expects_response": { - "default": true, - "title": "Expects Response", - "type": "boolean" - }, - "execution_mode": { - "default": "blocking", - "enum": [ - "blocking", - "fire_and_forget", - "background" - ], - "title": "Execution Mode", + "content": { + "description": "The full current text content.", + "title": "Content", "type": "string" } }, "required": [ - "tool_id", + "knowledge_source_id", "workspace_id", "name", - "tool_type", - "method", - "url", + "file_name", + "size_bytes", + "chunk_count", + "revision_number", "created_at", "updated_at", - "content_type", - "timeout_seconds", - "error_handling" + "content" ], "type": "object" } @@ -7855,12 +7654,12 @@ } }, "tags": [ - "Agent Tools" + "Knowledge Sources" ] }, "delete": { - "summary": "Delete Tool", - "description": "Delete a tool. Returns 409 while any agent's draft configuration still\nreferences it — check `GET /v1/agent/tools/{tool_id}/agents` and detach it\nvia the agent config first, so a delete can never silently change agent\nbehavior.", + "summary": "Delete Knowledge Source", + "description": "Delete a knowledge source. Returns 409 while any agent still references it\n— in its draft or in its currently published version (sessions resolve\nsources at call time, so deleting a published reference would change a\nrunning agent). Check `GET /v1/agent/knowledge-sources/{source_id}/agents`,\ndetach via the agent config, and republish if needed before deleting.", "security": [ { "BearerAuth": [] @@ -7869,11 +7668,11 @@ "parameters": [ { "in": "path", - "name": "tool_id", + "name": "source_id", "description": "", "required": true, "schema": { - "title": "Tool Id", + "title": "Source Id", "type": "string" }, "deprecated": false @@ -8034,14 +7833,14 @@ } }, "tags": [ - "Agent Tools" + "Knowledge Sources" ] } }, - "/v1/agent/tools/{tool_id}/agents": { + "/v1/agent/knowledge-sources/{source_id}/agents": { "get": { - "summary": "List Agents Using Tool", - "description": "Every agent whose draft configuration references this tool — the\npre-flight check before a delete. Published versions keep executing their\nfrozen tool snapshot, so only draft references block deletion.", + "summary": "List Agents Using Knowledge Source", + "description": "Every agent that references this source in its draft or currently\npublished configuration — the pre-flight check before a delete.", "security": [ { "BearerAuth": [] @@ -8050,11 +7849,11 @@ "parameters": [ { "in": "path", - "name": "tool_id", + "name": "source_id", "description": "", "required": true, "schema": { - "title": "Tool Id", + "title": "Source Id", "type": "string" }, "deprecated": false @@ -8203,14 +8002,14 @@ } }, "tags": [ - "Agent Tools" + "Knowledge Sources" ] } }, - "/v1/agent/phone-numbers": { + "/v1/agent/tools": { "get": { - "summary": "List Phone Numbers", - "description": "List your team's phone numbers, newest first. Released numbers are gone\nfor good and never appear. Look an id up by E.164 with `phone_number`, or\nfilter with `agent_id` to see one agent's numbers. Paginate with `cursor`\n(follow `next_cursor` while `has_more` is true) or with `page` for offset\npagination with a `total` count — the two are mutually exclusive.", + "summary": "List Tools", + "description": "List your team's tools, newest first. Filter with `agent_id` to see one\nagent's attached tools. Paginate with `cursor` (follow `next_cursor` while\n`has_more` is true) or with `page` for offset pagination with a `total`\ncount — the two are mutually exclusive.", "security": [ { "BearerAuth": [] @@ -8219,8 +8018,8 @@ "parameters": [ { "in": "query", - "name": "phone_number", - "description": "Exact E.164 match, e.g. +14155550123.", + "name": "search", + "description": "Case-insensitive match on name.", "required": false, "schema": { "anyOf": [ @@ -8232,14 +8031,14 @@ } ], "default": null, - "title": "Phone Number" + "title": "Search" }, "deprecated": false }, { "in": "query", "name": "agent_id", - "description": "Only numbers bound to this agent.", + "description": "Only tools attached to this agent's draft config.", "required": false, "schema": { "anyOf": [ @@ -8329,11 +8128,11 @@ "application/json": { "schema": { "properties": { - "phone_numbers": { + "tools": { "items": { - "$ref": "#/components/schemas/PublicPhoneNumberEntity" + "$ref": "#/components/schemas/PublicAgentToolSummary" }, - "title": "Phone Numbers", + "title": "Tools", "type": "array" }, "has_more": { @@ -8366,7 +8165,7 @@ } }, "required": [ - "phone_numbers", + "tools", "has_more" ], "type": "object" @@ -8524,12 +8323,12 @@ } }, "tags": [ - "Phone Numbers" + "Agent Tools" ] }, "post": { - "summary": "Purchase or Import Phone Number", - "description": "`provider` discriminates two variants. `twilio` buys a number from the\nmanaged inventory: it lands in your default workspace, any `agent_id` you\nbind must live there too, and billing is the monthly price charged in\ndaily slices; the first day is charged before anything is bought (402\ncosts you nothing) and the daily run advances it from there. `sip`\nimports a number you already own at your carrier: point your trunk's\norigination at our SIP host, provide inbound authentication (digest\nand/or source CIDRs) and optionally a termination host so the number can\nplace calls; nothing is rented and there is no monthly fee. Either way,\n409 means the number is already on the platform; 502 means the provider\nor trunk provisioning refused, and the number stays visible with status\n`error` and is safe to release.", + "summary": "Create Tool", + "description": "Create a workspace tool. `tool_type: webhook` calls your HTTP endpoint\nduring conversations; `tool_type: client` is executed by your own frontend\nthrough the SDK (no URL). Attach it to agents via the agent config's\n`tools.tool_ids`. Credential headers (authorization_bearer /\nauthorization_basic) are write-only and read back as `has_secret`.", "security": [ { "BearerAuth": [] @@ -8540,14 +8339,7 @@ "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/PublicPhoneNumberPurchasePayload" - }, - { - "$ref": "#/components/schemas/PublicSipNumberImportPayload" - } - ] + "$ref": "#/components/schemas/PublicAgentToolCreatePayload" } } } @@ -8560,34 +8352,154 @@ "application/json": { "schema": { "properties": { - "phone_number_id": { - "title": "Phone Number Id", + "tool_id": { + "title": "Tool Id", "type": "string" }, "workspace_id": { "title": "Workspace Id", "type": "string" }, - "phone_number": { - "description": "E.164, e.g. +14155550123.", - "title": "Phone Number", + "name": { + "title": "Name", "type": "string" }, - "provider": { + "description": { + "default": "", + "title": "Description", + "type": "string" + }, + "tool_type": { "enum": [ - "livekit", - "twilio", - "sip" + "webhook", + "client" ], - "title": "Provider", + "title": "Tool Type", "type": "string" }, - "label": { + "method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "title": "Method", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + }, + "used_by": { + "default": 0, + "description": "How many agents reference this tool in their draft configuration.", + "title": "Used By", + "type": "integer" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "arguments": { + "items": { + "$ref": "#/components/schemas/AgentWebhookArgumentPayload" + }, + "title": "Arguments", + "type": "array" + }, + "content_type": { + "title": "Content Type", + "type": "string" + }, + "body_template": { "default": "", - "title": "Label", + "title": "Body Template", "type": "string" }, - "agent_id": { + "headers": { + "items": { + "$ref": "#/components/schemas/PublicToolHeader" + }, + "title": "Headers", + "type": "array" + }, + "timeout_seconds": { + "title": "Timeout Seconds", + "type": "integer" + }, + "error_handling": { + "enum": [ + "passthrough", + "hide" + ], + "title": "Error Handling", + "type": "string" + }, + "mock_responses": { + "items": { + "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" + }, + "title": "Mock Responses", + "type": "array" + }, + "expects_response": { + "default": true, + "title": "Expects Response", + "type": "boolean" + }, + "execution_mode": { + "default": "blocking", + "enum": [ + "blocking", + "fire_and_forget", + "background" + ], + "title": "Execution Mode", + "type": "string" + } + }, + "required": [ + "tool_id", + "workspace_id", + "name", + "tool_type", + "method", + "url", + "created_at", + "updated_at", + "content_type", + "timeout_seconds", + "error_handling" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { "anyOf": [ { "type": "string" @@ -8596,163 +8508,262 @@ "type": "null" } ], - "description": "Inbound calls route to this agent; unbound numbers ring busy.", - "title": "Agent Id" + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "422": { + "description": "", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "cold_transfer_use_original_caller": { + "message": { + "title": "Message", + "type": "string" + }, + "reason": { "anyOf": [ { - "type": "boolean" + "type": "string" }, { "type": "null" } ], "default": null, - "description": "Managed `twilio` numbers: whether a transfer target, cold or warm, sees the original caller's number (true) or this number (false). `null` for imported `sip` numbers, whose carrier owns the setting.", - "title": "Cold Transfer Use Original Caller" + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "cold_transfer_use_original_caller_effective": { + "message": { + "title": "Message", + "type": "string" + }, + "reason": { "anyOf": [ { - "type": "boolean" + "type": "string" }, { "type": "null" } ], "default": null, - "description": "The cold-transfer policy the carrier has confirmed; `null` while unknown or for imported `sip` numbers. Warm transfers follow `cold_transfer_use_original_caller` directly from the next call.", - "title": "Cold Transfer Use Original Caller Effective" - }, - "caller_id_sync_status": { - "default": "carrier_managed", - "description": "`synced` once the carrier confirmed the policy, `pending` while it is being applied, `error` when the last attempt failed (send `retry_caller_id_sync`), `carrier_managed` for imported `sip` numbers.", - "enum": [ - "synced", - "pending", - "error", - "carrier_managed" - ], - "title": "Caller Id Sync Status", + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Agent Tools" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Create Webhook Tool", + "source": "curl --request POST \\\n --url https://api.fish.audio/v1/agent/tools \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"name\": \"Book appointment\",\n \"description\": \"Creates an appointment in the scheduling system.\",\n \"method\": \"POST\",\n \"url\": \"https://api.example.com/appointments\",\n \"arguments\": [\n {\"name\": \"date\", \"description\": \"Appointment date, YYYY-MM-DD\"}\n ],\n \"body_template\": \"{\\\"date\\\": \\\"{{date}}\\\"}\",\n \"headers\": [\n {\"name\": \"Authorization\", \"value\": \"Bearer \", \"kind\": \"authorization_bearer\"}\n ]\n }'" + } + ] + } + }, + "/v1/agent/tools/{tool_id}": { + "get": { + "summary": "Get Tool", + "description": "Fetch one tool's full definition. Credential header values are never\nreturned — each credential header reports `has_secret` instead.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "tool_id", + "description": "", + "required": true, + "schema": { + "title": "Tool Id", + "type": "string" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "tool_id": { + "title": "Tool Id", "type": "string" }, - "caller_id_sync_error": { + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { "default": "", - "description": "Why the last synchronization failed; empty otherwise.", - "title": "Caller Id Sync Error", + "title": "Description", "type": "string" }, - "status": { + "tool_type": { "enum": [ - "provisioning", - "active", - "error", - "released" + "webhook", + "client" ], - "title": "Status", + "title": "Tool Type", "type": "string" }, - "status_detail": { - "default": "", - "description": "What failed when status is `error`; empty otherwise.", - "title": "Status Detail", + "method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "title": "Method", "type": "string" }, - "supports_outbound": { - "default": false, - "description": "Whether the number can place calls (outbound and warm-transfer consult legs): managed `twilio` always, imported `sip` only when a termination was configured.", - "title": "Supports Outbound", - "type": "boolean" + "url": { + "title": "Url", + "type": "string" }, - "termination_uri": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: the customer trunk's termination host.", - "title": "Termination Uri" + "used_by": { + "default": 0, + "description": "How many agents reference this tool in their draft configuration.", + "title": "Used By", + "type": "integer" }, - "inbound_auth_username": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: the inbound digest username. Passwords are never echoed.", - "title": "Inbound Auth Username" + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" }, - "inbound_allowed_addresses": { - "description": "Imported `sip` numbers: allowed source IPs/CIDRs.", + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "arguments": { "items": { - "type": "string" + "$ref": "#/components/schemas/AgentWebhookArgumentPayload" }, - "title": "Inbound Allowed Addresses", + "title": "Arguments", "type": "array" }, - "termination_transport": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: termination transport.", - "title": "Termination Transport" + "content_type": { + "title": "Content Type", + "type": "string" }, - "termination_auth_username": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: the termination digest username. Passwords are never echoed.", - "title": "Termination Auth Username" + "body_template": { + "default": "", + "title": "Body Template", + "type": "string" }, - "termination_headers": { - "additionalProperties": { - "type": "string" + "headers": { + "items": { + "$ref": "#/components/schemas/PublicToolHeader" }, - "description": "Imported `sip` numbers: custom SIP headers added to every outbound INVITE through the termination.", - "title": "Termination Headers", - "type": "object" + "title": "Headers", + "type": "array" }, - "created_at": { - "format": "date-time", - "title": "Created At", + "timeout_seconds": { + "title": "Timeout Seconds", + "type": "integer" + }, + "error_handling": { + "enum": [ + "passthrough", + "hide" + ], + "title": "Error Handling", "type": "string" }, - "updated_at": { - "format": "date-time", - "title": "Updated At", + "mock_responses": { + "items": { + "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" + }, + "title": "Mock Responses", + "type": "array" + }, + "expects_response": { + "default": true, + "title": "Expects Response", + "type": "boolean" + }, + "execution_mode": { + "default": "blocking", + "enum": [ + "blocking", + "fire_and_forget", + "background" + ], + "title": "Execution Mode", "type": "string" } }, "required": [ - "phone_number_id", + "tool_id", "workspace_id", - "phone_number", - "provider", - "agent_id", - "status", + "name", + "tool_type", + "method", + "url", "created_at", - "updated_at" + "updated_at", + "content_type", + "timeout_seconds", + "error_handling" ], "type": "object" } @@ -8796,8 +8807,8 @@ } } }, - "402": { - "description": "No payment -- see charging schemes", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -8833,8 +8844,8 @@ } } }, - "403": { - "description": "Request forbidden -- authorization will not help", + "503": { + "description": "The server cannot process the request due to a high load", "headers": {}, "content": { "application/json": { @@ -8869,9 +8880,185 @@ } } } + } + }, + "tags": [ + "Agent Tools" + ] + }, + "patch": { + "summary": "Update Tool", + "description": "Patch tool fields; omitted fields keep their value (null is rejected —\nsend an empty string to clear a text field). `headers` replaces the header\nlist wholesale — include credential values again whenever you send it,\nsince reads never return them. Attached agents' drafts pick up the change\nimmediately; published versions stay frozen until re-published.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "tool_id", + "description": "", + "required": true, + "schema": { + "title": "Tool Id", + "type": "string" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicAgentToolUpdatePayload" + } + } + } + }, + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "tool_id": { + "title": "Tool Id", + "type": "string" + }, + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "default": "", + "title": "Description", + "type": "string" + }, + "tool_type": { + "enum": [ + "webhook", + "client" + ], + "title": "Tool Type", + "type": "string" + }, + "method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "title": "Method", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + }, + "used_by": { + "default": 0, + "description": "How many agents reference this tool in their draft configuration.", + "title": "Used By", + "type": "integer" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "arguments": { + "items": { + "$ref": "#/components/schemas/AgentWebhookArgumentPayload" + }, + "title": "Arguments", + "type": "array" + }, + "content_type": { + "title": "Content Type", + "type": "string" + }, + "body_template": { + "default": "", + "title": "Body Template", + "type": "string" + }, + "headers": { + "items": { + "$ref": "#/components/schemas/PublicToolHeader" + }, + "title": "Headers", + "type": "array" + }, + "timeout_seconds": { + "title": "Timeout Seconds", + "type": "integer" + }, + "error_handling": { + "enum": [ + "passthrough", + "hide" + ], + "title": "Error Handling", + "type": "string" + }, + "mock_responses": { + "items": { + "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" + }, + "title": "Mock Responses", + "type": "array" + }, + "expects_response": { + "default": true, + "title": "Expects Response", + "type": "boolean" + }, + "execution_mode": { + "default": "blocking", + "enum": [ + "blocking", + "fire_and_forget", + "background" + ], + "title": "Execution Mode", + "type": "string" + } + }, + "required": [ + "tool_id", + "workspace_id", + "name", + "tool_type", + "method", + "url", + "created_at", + "updated_at", + "content_type", + "timeout_seconds", + "error_handling" + ], + "type": "object" + } + } + } }, - "404": { - "description": "Nothing matches the given URI", + "400": { + "description": "Bad request syntax or unsupported method", "headers": {}, "content": { "application/json": { @@ -8907,8 +9094,8 @@ } } }, - "409": { - "description": "Request conflict", + "401": { + "description": "No permission -- see authorization schemes", "headers": {}, "content": { "application/json": { @@ -8944,8 +9131,8 @@ } } }, - "422": { - "description": "", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -8981,8 +9168,8 @@ } } }, - "502": { - "description": "Invalid responses from another server/proxy", + "422": { + "description": "", "headers": {}, "content": { "application/json": { @@ -9057,14 +9244,12 @@ } }, "tags": [ - "Phone Numbers" + "Agent Tools" ] - } - }, - "/v1/agent/phone-numbers/{phone_number_id}": { - "get": { - "summary": "Get Phone Number", - "description": "Fetch one phone number, including its current agent binding and\nprovisioning status.", + }, + "delete": { + "summary": "Delete Tool", + "description": "Delete a tool. Returns 409 while any agent's draft configuration still\nreferences it — check `GET /v1/agent/tools/{tool_id}/agents` and detach it\nvia the agent config first, so a delete can never silently change agent\nbehavior.", "security": [ { "BearerAuth": [] @@ -9073,52 +9258,37 @@ "parameters": [ { "in": "path", - "name": "phone_number_id", + "name": "tool_id", "description": "", "required": true, "schema": { - "title": "Phone Number Id", + "title": "Tool Id", "type": "string" }, "deprecated": false } ], "responses": { - "200": { - "description": "Request fulfilled, document follows", + "204": { + "description": "Request fulfilled, nothing follows", + "headers": {} + }, + "401": { + "description": "No permission -- see authorization schemes", "headers": {}, "content": { "application/json": { "schema": { "properties": { - "phone_number_id": { - "title": "Phone Number Id", - "type": "string" - }, - "workspace_id": { - "title": "Workspace Id", - "type": "string" - }, - "phone_number": { - "description": "E.164, e.g. +14155550123.", - "title": "Phone Number", - "type": "string" - }, - "provider": { - "enum": [ - "livekit", - "twilio", - "sip" - ], - "title": "Provider", - "type": "string" + "status": { + "title": "Status", + "type": "integer" }, - "label": { - "default": "", - "title": "Label", + "message": { + "title": "Message", "type": "string" }, - "agent_id": { + "reason": { "anyOf": [ { "type": "string" @@ -9127,89 +9297,35 @@ "type": "null" } ], - "description": "Inbound calls route to this agent; unbound numbers ring busy.", - "title": "Agent Id" - }, - "cold_transfer_use_original_caller": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Managed `twilio` numbers: whether a transfer target, cold or warm, sees the original caller's number (true) or this number (false). `null` for imported `sip` numbers, whose carrier owns the setting.", - "title": "Cold Transfer Use Original Caller" - }, - "cold_transfer_use_original_caller_effective": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], "default": null, - "description": "The cold-transfer policy the carrier has confirmed; `null` while unknown or for imported `sip` numbers. Warm transfers follow `cold_transfer_use_original_caller` directly from the next call.", - "title": "Cold Transfer Use Original Caller Effective" - }, - "caller_id_sync_status": { - "default": "carrier_managed", - "description": "`synced` once the carrier confirmed the policy, `pending` while it is being applied, `error` when the last attempt failed (send `retry_caller_id_sync`), `carrier_managed` for imported `sip` numbers.", - "enum": [ - "synced", - "pending", - "error", - "carrier_managed" - ], - "title": "Caller Id Sync Status", - "type": "string" - }, - "caller_id_sync_error": { - "default": "", - "description": "Why the last synchronization failed; empty otherwise.", - "title": "Caller Id Sync Error", - "type": "string" - }, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { "status": { - "enum": [ - "provisioning", - "active", - "error", - "released" - ], "title": "Status", - "type": "string" + "type": "integer" }, - "status_detail": { - "default": "", - "description": "What failed when status is `error`; empty otherwise.", - "title": "Status Detail", + "message": { + "title": "Message", "type": "string" }, - "supports_outbound": { - "default": false, - "description": "Whether the number can place calls (outbound and warm-transfer consult legs): managed `twilio` always, imported `sip` only when a termination was configured.", - "title": "Supports Outbound", - "type": "boolean" - }, - "termination_uri": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: the customer trunk's termination host.", - "title": "Termination Uri" - }, - "inbound_auth_username": { + "reason": { "anyOf": [ { "type": "string" @@ -9219,18 +9335,34 @@ } ], "default": null, - "description": "Imported `sip` numbers: the inbound digest username. Passwords are never echoed.", - "title": "Inbound Auth Username" + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "409": { + "description": "Request conflict", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "inbound_allowed_addresses": { - "description": "Imported `sip` numbers: allowed source IPs/CIDRs.", - "items": { - "type": "string" - }, - "title": "Inbound Allowed Addresses", - "type": "array" + "message": { + "title": "Message", + "type": "string" }, - "termination_transport": { + "reason": { "anyOf": [ { "type": "string" @@ -9240,10 +9372,34 @@ } ], "default": null, - "description": "Imported `sip` numbers: termination transport.", - "title": "Termination Transport" + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "termination_auth_username": { + "message": { + "title": "Message", + "type": "string" + }, + "reason": { "anyOf": [ { "type": "string" @@ -9253,37 +9409,70 @@ } ], "default": null, - "description": "Imported `sip` numbers: the termination digest username. Passwords are never echoed.", - "title": "Termination Auth Username" - }, - "termination_headers": { - "additionalProperties": { - "type": "string" + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Agent Tools" + ] + } + }, + "/v1/agent/tools/{tool_id}/agents": { + "get": { + "summary": "List Agents Using Tool", + "description": "Every agent whose draft configuration references this tool — the\npre-flight check before a delete. Published versions keep executing their\nfrozen tool snapshot, so only draft references block deletion.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "tool_id", + "description": "", + "required": true, + "schema": { + "title": "Tool Id", + "type": "string" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "agents": { + "description": "Agents that reference this resource in their configuration.", + "items": { + "$ref": "#/components/schemas/PublicDependentAgent" }, - "description": "Imported `sip` numbers: custom SIP headers added to every outbound INVITE through the termination.", - "title": "Termination Headers", - "type": "object" - }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" + "title": "Agents", + "type": "array" }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" + "total": { + "title": "Total", + "type": "integer" } }, "required": [ - "phone_number_id", - "workspace_id", - "phone_number", - "provider", - "agent_id", - "status", - "created_at", - "updated_at" + "agents", + "total" ], "type": "object" } @@ -9403,12 +9592,14 @@ } }, "tags": [ - "Phone Numbers" + "Agent Tools" ] - }, - "patch": { - "summary": "Update Phone Number", - "description": "Change the label and/or repoint the number at another agent — the\ndeployment-pipeline move (rebind from the staging agent to the production\none). Send `agent_id: null` to unbind; unbound numbers ring busy. The\nagent must live in the number's workspace. Rebinding is a routing-table\nupdate resolved on the next inbound call; nothing about the number itself\nis reprovisioned.\n\nManaged `twilio` numbers also accept `cold_transfer_use_original_caller`,\nwhich picks the number a transfer target sees (cold and warm alike), and\n`retry_caller_id_sync` to re-apply it after a failed synchronization; read\n`caller_id_sync_status` on the response. Imported `sip` numbers return 409\nfor either field because their carrier owns the setting.", + } + }, + "/v1/agent/tests": { + "get": { + "summary": "List Tests", + "description": "List your team's tests, newest first. Filter with `agent_id` to see the\ntests attached to one agent. Paginate with `cursor` (follow `next_cursor`\nwhile `has_more` is true) or with `page` for offset pagination with a\n`total` count. The two are mutually exclusive.", "security": [ { "BearerAuth": [] @@ -9416,27 +9607,133 @@ ], "parameters": [ { - "in": "path", - "name": "phone_number_id", - "description": "", - "required": true, - "schema": { - "title": "Phone Number Id", - "type": "string" + "in": "query", + "name": "search", + "description": "Case-insensitive match on name.", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Search" + }, + "deprecated": false + }, + { + "in": "query", + "name": "agent_id", + "description": "Only tests attached to this agent.", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Id" + }, + "deprecated": false + }, + { + "in": "query", + "name": "test_type", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Test Type" + }, + "deprecated": false + }, + { + "in": "query", + "name": "cursor", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cursor" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page", + "description": "1-based page number, mutually exclusive with cursor.", + "required": false, + "schema": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Page" + }, + "deprecated": false + }, + { + "in": "query", + "name": "include_total", + "description": "", + "required": false, + "schema": { + "default": false, + "title": "Include Total", + "type": "boolean" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page_size", + "description": "", + "required": false, + "schema": { + "default": 30, + "maximum": 100, + "minimum": 1, + "title": "Page Size", + "type": "integer" }, "deprecated": false } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicPhoneNumberUpdatePayload" - } - } - } - }, "responses": { "200": { "description": "Request fulfilled, document follows", @@ -9445,34 +9742,18 @@ "application/json": { "schema": { "properties": { - "phone_number_id": { - "title": "Phone Number Id", - "type": "string" - }, - "workspace_id": { - "title": "Workspace Id", - "type": "string" - }, - "phone_number": { - "description": "E.164, e.g. +14155550123.", - "title": "Phone Number", - "type": "string" - }, - "provider": { - "enum": [ - "livekit", - "twilio", - "sip" - ], - "title": "Provider", - "type": "string" + "tests": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestSummary" + }, + "title": "Tests", + "type": "array" }, - "label": { - "default": "", - "title": "Label", - "type": "string" + "has_more": { + "title": "Has More", + "type": "boolean" }, - "agent_id": { + "next_cursor": { "anyOf": [ { "type": "string" @@ -9481,89 +9762,47 @@ "type": "null" } ], - "description": "Inbound calls route to this agent; unbound numbers ring busy.", - "title": "Agent Id" - }, - "cold_transfer_use_original_caller": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], "default": null, - "description": "Managed `twilio` numbers: whether a transfer target, cold or warm, sees the original caller's number (true) or this number (false). `null` for imported `sip` numbers, whose carrier owns the setting.", - "title": "Cold Transfer Use Original Caller" + "title": "Next Cursor" }, - "cold_transfer_use_original_caller_effective": { + "total": { "anyOf": [ { - "type": "boolean" + "type": "integer" }, { "type": "null" } ], "default": null, - "description": "The cold-transfer policy the carrier has confirmed; `null` while unknown or for imported `sip` numbers. Warm transfers follow `cold_transfer_use_original_caller` directly from the next call.", - "title": "Cold Transfer Use Original Caller Effective" - }, - "caller_id_sync_status": { - "default": "carrier_managed", - "description": "`synced` once the carrier confirmed the policy, `pending` while it is being applied, `error` when the last attempt failed (send `retry_caller_id_sync`), `carrier_managed` for imported `sip` numbers.", - "enum": [ - "synced", - "pending", - "error", - "carrier_managed" - ], - "title": "Caller Id Sync Status", - "type": "string" - }, - "caller_id_sync_error": { - "default": "", - "description": "Why the last synchronization failed; empty otherwise.", - "title": "Caller Id Sync Error", - "type": "string" - }, + "title": "Total" + } + }, + "required": [ + "tests", + "has_more" + ], + "type": "object" + } + } + } + }, + "400": { + "description": "Bad request syntax or unsupported method", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { "status": { - "enum": [ - "provisioning", - "active", - "error", - "released" - ], "title": "Status", - "type": "string" + "type": "integer" }, - "status_detail": { - "default": "", - "description": "What failed when status is `error`; empty otherwise.", - "title": "Status Detail", + "message": { + "title": "Message", "type": "string" }, - "supports_outbound": { - "default": false, - "description": "Whether the number can place calls (outbound and warm-transfer consult legs): managed `twilio` always, imported `sip` only when a termination was configured.", - "title": "Supports Outbound", - "type": "boolean" - }, - "termination_uri": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: the customer trunk's termination host.", - "title": "Termination Uri" - }, - "inbound_auth_username": { + "reason": { "anyOf": [ { "type": "string" @@ -9573,71 +9812,12 @@ } ], "default": null, - "description": "Imported `sip` numbers: the inbound digest username. Passwords are never echoed.", - "title": "Inbound Auth Username" - }, - "inbound_allowed_addresses": { - "description": "Imported `sip` numbers: allowed source IPs/CIDRs.", - "items": { - "type": "string" - }, - "title": "Inbound Allowed Addresses", - "type": "array" - }, - "termination_transport": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: termination transport.", - "title": "Termination Transport" - }, - "termination_auth_username": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Imported `sip` numbers: the termination digest username. Passwords are never echoed.", - "title": "Termination Auth Username" - }, - "termination_headers": { - "additionalProperties": { - "type": "string" - }, - "description": "Imported `sip` numbers: custom SIP headers added to every outbound INVITE through the termination.", - "title": "Termination Headers", - "type": "object" - }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" + "title": "Reason" } }, "required": [ - "phone_number_id", - "workspace_id", - "phone_number", - "provider", - "agent_id", "status", - "created_at", - "updated_at" + "message" ], "type": "object" } @@ -9718,8 +9898,8 @@ } } }, - "409": { - "description": "Request conflict", + "503": { + "description": "The server cannot process the request due to a high load", "headers": {}, "content": { "application/json": { @@ -9754,46 +9934,173 @@ } } } - }, - "422": { - "description": "", + } + }, + "tags": [ + "Agent Tests" + ] + }, + "post": { + "summary": "Create Test", + "description": "Create a test: `next_reply` judges the agent's next reply against an\nexpectation, `tool` checks the tool the agent calls next, and `simulation`\nlets a simulated user hold a whole conversation that is scored against\nsuccess conditions. Pass `agent_ids` to attach the test right away.\n\nThe body of `GET /v1/agent/tests/{test_id}` posts back as is, so tests can\nbe exported and imported. Tool references use your workspace's tool ids\n(list them with `GET /v1/agent/agents/{agent_id}/test-tools`), so an export\nimports cleanly within the same team and needs its tool ids remapped\nelsewhere.", + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicAgentTestCreatePayload" + } + } + } + }, + "responses": { + "201": { + "description": "Document created, URL follows", "headers": {}, "content": { "application/json": { "schema": { "properties": { - "status": { - "title": "Status", - "type": "integer" + "test_id": { + "title": "Test Id", + "type": "string" }, - "message": { - "title": "Message", + "workspace_id": { + "title": "Workspace Id", "type": "string" }, - "reason": { + "name": { + "title": "Name", + "type": "string" + }, + "test_type": { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "title": "Test Type", + "type": "string" + }, + "agent_ids": { + "description": "Agents this test is attached to.", + "items": { + "type": "string" + }, + "title": "Agent Ids", + "type": "array" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "conversation": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestMessage" + }, + "title": "Conversation", + "type": "array" + }, + "expectation": { + "default": "", + "title": "Expectation", + "type": "string" + }, + "success_examples": { + "items": { + "type": "string" + }, + "title": "Success Examples", + "type": "array" + }, + "failure_examples": { + "items": { + "type": "string" + }, + "title": "Failure Examples", + "type": "array" + }, + "referenced_tool": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/AgentTestReferencedTool" }, { "type": "null" } ], - "default": null, - "title": "Reason" + "default": null + }, + "tool_parameters": { + "items": { + "$ref": "#/components/schemas/AgentTestToolParameter" + }, + "title": "Tool Parameters", + "type": "array" + }, + "verify_absence": { + "default": false, + "title": "Verify Absence", + "type": "boolean" + }, + "simulation": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentTestSimulationConfig" + }, + { + "type": "null" + } + ], + "default": null + }, + "dynamic_variables": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + "title": "Dynamic Variables", + "type": "object" } }, "required": [ - "status", - "message" + "test_id", + "workspace_id", + "name", + "test_type", + "created_at", + "updated_at" ], "type": "object" } } } }, - "503": { - "description": "The server cannot process the request due to a high load", + "400": { + "description": "Bad request syntax or unsupported method", "headers": {}, "content": { "application/json": { @@ -9828,37 +10135,6 @@ } } } - } - }, - "tags": [ - "Phone Numbers" - ] - }, - "delete": { - "summary": "Release Phone Number", - "description": "Release a number back to the provider's inventory and stop its daily\nbilling. This is irreversible: anyone — including other platforms — can\nbuy the number afterwards, so callers who saved it may reach a stranger.\nThe number disappears from this API immediately.", - "security": [ - { - "BearerAuth": [] - } - ], - "parameters": [ - { - "in": "path", - "name": "phone_number_id", - "description": "", - "required": true, - "schema": { - "title": "Phone Number Id", - "type": "string" - }, - "deprecated": false - } - ], - "responses": { - "204": { - "description": "Request fulfilled, nothing follows", - "headers": {} }, "401": { "description": "No permission -- see authorization schemes", @@ -9897,8 +10173,8 @@ } } }, - "404": { - "description": "Nothing matches the given URI", + "422": { + "description": "", "headers": {}, "content": { "application/json": { @@ -9973,14 +10249,21 @@ } }, "tags": [ - "Phone Numbers" + "Agent Tests" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Create Simulation Test", + "source": "curl --request POST \\\n --url https://api.fish.audio/v1/agent/tests \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"name\": \"Reschedules an appointment\",\n \"test_type\": \"simulation\",\n \"agent_ids\": [\"\"],\n \"simulation\": {\n \"scenario\": \"You are Jane. Move your Tuesday cleaning to Thursday afternoon.\",\n \"max_turns\": 10,\n \"success_conditions\": [\n {\"name\": \"rescheduled\", \"description\": \"The agent confirms the new Thursday slot.\"}\n ],\n \"tool_mocks\": {\n \"strategy\": \"all\",\n \"tools\": [\n {\n \"tool\": {\"id\": \"\", \"name\": \"Book appointment\", \"type\": \"webhook\"},\n \"result\": {\"status\": \"confirmed\"}\n }\n ]\n }\n }\n }'" + } ] } }, - "/v1/agent/phone-calls": { - "post": { - "summary": "Create Phone Call", - "description": "Place an outbound call from one of your phone numbers. Platform numbers\ndial US, Canada or Japan destinations, and a domestic trunk 0 after +81\n(e.g. +81080...) is accepted and normalized to E.164 (+8180...).\nImported SIP numbers dial through their own termination and may attach\ncustom INVITE headers with `sip_headers`. Returns immediately with the\nsession queued for dialing. Subscribe to the `phone_call.dial_finished`\nwebhook or poll `GET /v1/agent/sessions/{session_id}` for the dial\noutcome and the leg's `sip_call_id`. Ringing is never billed: metering\nstarts when the callee answers.\n\nErrors carry a machine-readable `reason` (e.g. `destination_not_allowed`,\n`insufficient_credit`, `concurrency_limit_exceeded`).", + "/v1/agent/tests/{test_id}": { + "get": { + "summary": "Get Test", + "description": "Fetch one test's full definition. The body is also the export format: post\nit to `POST /v1/agent/tests` to create a copy.", "security": [ { "BearerAuth": [] @@ -9988,56 +10271,152 @@ ], "parameters": [ { - "in": "header", - "name": "idempotency-key", - "description": "Retry-safe replay key: the same key with the same body returns the call already placed instead of dialing again (24h window).", - "required": false, + "in": "path", + "name": "test_id", + "description": "", + "required": true, "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Idempotency-Key" + "title": "Test Id", + "type": "string" }, "deprecated": false } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PhoneCallCreatePayload" - } - } - } - }, "responses": { - "201": { - "description": "Document created, URL follows", + "200": { + "description": "Request fulfilled, document follows", "headers": {}, "content": { "application/json": { "schema": { "properties": { - "session_id": { - "title": "Session Id", + "test_id": { + "title": "Test Id", "type": "string" }, - "status": { - "const": "queued", - "default": "queued", - "title": "Status", + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "test_type": { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "title": "Test Type", + "type": "string" + }, + "agent_ids": { + "description": "Agents this test is attached to.", + "items": { + "type": "string" + }, + "title": "Agent Ids", + "type": "array" + }, + "created_at": { + "format": "date-time", + "title": "Created At", "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "conversation": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestMessage" + }, + "title": "Conversation", + "type": "array" + }, + "expectation": { + "default": "", + "title": "Expectation", + "type": "string" + }, + "success_examples": { + "items": { + "type": "string" + }, + "title": "Success Examples", + "type": "array" + }, + "failure_examples": { + "items": { + "type": "string" + }, + "title": "Failure Examples", + "type": "array" + }, + "referenced_tool": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentTestReferencedTool" + }, + { + "type": "null" + } + ], + "default": null + }, + "tool_parameters": { + "items": { + "$ref": "#/components/schemas/AgentTestToolParameter" + }, + "title": "Tool Parameters", + "type": "array" + }, + "verify_absence": { + "default": false, + "title": "Verify Absence", + "type": "boolean" + }, + "simulation": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentTestSimulationConfig" + }, + { + "type": "null" + } + ], + "default": null + }, + "dynamic_variables": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + "title": "Dynamic Variables", + "type": "object" } }, "required": [ - "session_id" + "test_id", + "workspace_id", + "name", + "test_type", + "created_at", + "updated_at" ], "type": "object" } @@ -10081,8 +10460,8 @@ } } }, - "402": { - "description": "No payment -- see charging schemes", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -10118,8 +10497,8 @@ } } }, - "403": { - "description": "Request forbidden -- authorization will not help", + "503": { + "description": "The server cannot process the request due to a high load", "headers": {}, "content": { "application/json": { @@ -10154,46 +10533,186 @@ } } } - }, - "404": { - "description": "Nothing matches the given URI", + } + }, + "tags": [ + "Agent Tests" + ] + }, + "patch": { + "summary": "Update Test", + "description": "Patch test fields. Omitted fields keep their value and null is rejected,\nexcept `referenced_tool: null`, which turns a `tool` test into a check that\nthe agent calls no tool at all. Attach and detach agents with\n`PUT` and `DELETE /v1/agent/agents/{agent_id}/tests/{test_id}`.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "test_id", + "description": "", + "required": true, + "schema": { + "title": "Test Id", + "type": "string" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicAgentTestUpdatePayload" + } + } + } + }, + "responses": { + "200": { + "description": "Request fulfilled, document follows", "headers": {}, "content": { "application/json": { "schema": { "properties": { - "status": { - "title": "Status", - "type": "integer" + "test_id": { + "title": "Test Id", + "type": "string" }, - "message": { - "title": "Message", + "workspace_id": { + "title": "Workspace Id", "type": "string" }, - "reason": { + "name": { + "title": "Name", + "type": "string" + }, + "test_type": { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "title": "Test Type", + "type": "string" + }, + "agent_ids": { + "description": "Agents this test is attached to.", + "items": { + "type": "string" + }, + "title": "Agent Ids", + "type": "array" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "conversation": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestMessage" + }, + "title": "Conversation", + "type": "array" + }, + "expectation": { + "default": "", + "title": "Expectation", + "type": "string" + }, + "success_examples": { + "items": { + "type": "string" + }, + "title": "Success Examples", + "type": "array" + }, + "failure_examples": { + "items": { + "type": "string" + }, + "title": "Failure Examples", + "type": "array" + }, + "referenced_tool": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/AgentTestReferencedTool" }, { "type": "null" } ], - "default": null, - "title": "Reason" + "default": null + }, + "tool_parameters": { + "items": { + "$ref": "#/components/schemas/AgentTestToolParameter" + }, + "title": "Tool Parameters", + "type": "array" + }, + "verify_absence": { + "default": false, + "title": "Verify Absence", + "type": "boolean" + }, + "simulation": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentTestSimulationConfig" + }, + { + "type": "null" + } + ], + "default": null + }, + "dynamic_variables": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + "title": "Dynamic Variables", + "type": "object" } }, "required": [ - "status", - "message" + "test_id", + "workspace_id", + "name", + "test_type", + "created_at", + "updated_at" ], "type": "object" } } } }, - "409": { - "description": "Request conflict", + "401": { + "description": "No permission -- see authorization schemes", "headers": {}, "content": { "application/json": { @@ -10229,8 +10748,8 @@ } } }, - "422": { - "description": "", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -10266,8 +10785,8 @@ } } }, - "429": { - "description": "The user has sent too many requests in a given amount of time (\"rate limiting\")", + "422": { + "description": "", "headers": {}, "content": { "application/json": { @@ -10303,45 +10822,8 @@ } } }, - "502": { - "description": "Invalid responses from another server/proxy", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - }, - "503": { - "description": "The server cannot process the request due to a high load", + "503": { + "description": "The server cannot process the request due to a high load", "headers": {}, "content": { "application/json": { @@ -10379,20 +10861,12 @@ } }, "tags": [ - "Phone Calls" - ], - "x-codeSamples": [ - { - "lang": "bash", - "label": "Create Phone Call", - "source": "curl --request POST \\\n --url https://api.fish.audio/v1/agent/phone-calls \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'Idempotency-Key: ' \\\n --data '{\n \"agent_id\": \"\",\n \"phone_number_id\": \"\",\n \"to_number\": \"+14155550123\"\n }'" - } + "Agent Tests" ] - } - }, - "/v1/asr": { - "post": { - "summary": "Speech to Text", + }, + "delete": { + "summary": "Delete Test", + "description": "Delete a test and detach it from every agent. Its past runs stay readable.", "security": [ { "BearerAuth": [] @@ -10400,157 +10874,21 @@ ], "parameters": [ { - "in": "header", - "name": "model", - "description": "Specify which speech-to-text model to use.", - "required": false, + "in": "path", + "name": "test_id", + "description": "", + "required": true, "schema": { - "default": "transcribe-1", - "enum": [ - "transcribe-1", - "transcribe-1-pro" - ], - "title": "Model", + "title": "Test Id", "type": "string" }, "deprecated": false } ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "properties": { - "audio": { - "description": "Audio file to be converted to text", - "format": "binary", - "title": "Audio", - "type": "string" - }, - "language": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Optional hint. The language is auto-detected regardless; the detected language is returned as `language_code`.", - "title": "Language" - }, - "ignore_timestamps": { - "default": true, - "description": "Whether to return precise timestamps in the text, this will increase the latency in audio shorter than 30 seconds", - "title": "Ignore Timestamps", - "type": "boolean" - } - }, - "required": [ - "audio" - ], - "type": "object" - } - }, - "application/msgpack": { - "schema": { - "properties": { - "audio": { - "description": "Audio file to be converted to text", - "format": "binary", - "title": "Audio", - "type": "string" - }, - "language": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Optional hint. The language is auto-detected regardless; the detected language is returned as `language_code`.", - "title": "Language" - }, - "ignore_timestamps": { - "default": true, - "description": "Whether to return precise timestamps in the text, this will increase the latency in audio shorter than 30 seconds", - "title": "Ignore Timestamps", - "type": "boolean" - } - }, - "required": [ - "audio" - ], - "type": "object" - } - } - } - }, "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "text": { - "title": "Text", - "type": "string" - }, - "duration": { - "description": "Duration of the audio in seconds", - "title": "Duration", - "type": "number" - }, - "segments": { - "items": { - "$ref": "#/components/schemas/ASRSegment" - }, - "title": "Segments", - "type": "array" - }, - "language_code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Detected language as an ISO 639-1 code (e.g. `en`, `ja`). Omitted if no language is detected.", - "title": "Language Code" - }, - "language": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Detected language name (e.g. `English`). For display only; use `language_code` in code.", - "title": "Language" - } - }, - "required": [ - "text", - "duration", - "segments" - ], - "type": "object" - } - } - } + "204": { + "description": "Request fulfilled, nothing follows", + "headers": {} }, "401": { "description": "No permission -- see authorization schemes", @@ -10589,8 +10927,8 @@ } } }, - "402": { - "description": "No payment -- see charging schemes", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -10665,13 +11003,14 @@ } }, "tags": [ - "OpenAPI v1" + "Agent Tests" ] } }, - "/v1/tts": { - "post": { - "summary": "Text to Speech", + "/v1/agent/test-runs": { + "get": { + "summary": "List Test Runs", + "description": "Your team's test runs, newest first, filtered by agent, test, batch,\nstatus or test type. Items leave out the conversation and the judge's\noutput. Fetch one run with `GET /v1/agent/test-runs/{run_id}` for those.\nPaginate with `cursor` (follow `next_cursor` while `has_more` is true) or\nwith `page` for offset pagination with a `total` count. The two are\nmutually exclusive.", "security": [ { "BearerAuth": [] @@ -10679,53 +11018,269 @@ ], "parameters": [ { - "in": "header", - "name": "model", - "description": "Specify which TTS model to use. Use `s2.1-pro-free` for the free developer tier. `drama-3-preview` is a preview model; its behavior and availability may change. If omitted or set to an unrecognized value, the request falls back to `s2.1-pro`.", + "in": "query", + "name": "agent_id", + "description": "", "required": false, "schema": { - "default": "s2.1-pro", - "enum": [ - "s1", - "s2-pro", - "s2.1-pro", - "s2.1-pro-free", - "drama-3-preview" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } ], - "title": "Model", - "type": "string" + "default": null, + "title": "Agent Id" }, "deprecated": false - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TTSRequest" - } - }, - "application/msgpack": { - "schema": { - "$ref": "#/components/schemas/TTSRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": { - "Transfer-Encoding": { - "schema": { - "type": "string" - }, - "description": "chunked" - } - } }, - "401": { + { + "in": "query", + "name": "test_id", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Test Id" + }, + "deprecated": false + }, + { + "in": "query", + "name": "batch_id", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Batch Id" + }, + "deprecated": false + }, + { + "in": "query", + "name": "status", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "enum": [ + "queued", + "running", + "passed", + "failed", + "error" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Status" + }, + "deprecated": false + }, + { + "in": "query", + "name": "test_type", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Test Type" + }, + "deprecated": false + }, + { + "in": "query", + "name": "cursor", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cursor" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page", + "description": "1-based page number, mutually exclusive with cursor.", + "required": false, + "schema": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Page" + }, + "deprecated": false + }, + { + "in": "query", + "name": "include_total", + "description": "", + "required": false, + "schema": { + "default": false, + "title": "Include Total", + "type": "boolean" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page_size", + "description": "", + "required": false, + "schema": { + "default": 30, + "maximum": 100, + "minimum": 1, + "title": "Page Size", + "type": "integer" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "runs": { + "items": { + "$ref": "#/components/schemas/PublicAgentTestRunSummary" + }, + "title": "Runs", + "type": "array" + }, + "has_more": { + "title": "Has More", + "type": "boolean" + }, + "next_cursor": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Next Cursor" + }, + "total": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Total" + } + }, + "required": [ + "runs", + "has_more" + ], + "type": "object" + } + } + } + }, + "400": { + "description": "Bad request syntax or unsupported method", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "401": { "description": "No permission -- see authorization schemes", "headers": {}, "content": { @@ -10762,8 +11317,8 @@ } } }, - "402": { - "description": "No payment -- see charging schemes", + "404": { + "description": "Nothing matches the given URI", "headers": {}, "content": { "application/json": { @@ -10838,25 +11393,14 @@ } }, "tags": [ - "OpenAPI v1" - ], - "x-codeSamples": [ - { - "lang": "bash", - "label": "Single Speaker", - "source": "curl --request POST \\\n --url https://api.fish.audio/v1/tts \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: s2.1-pro-free' \\\n --data '{\n \"text\": \"Hello! Welcome to Fish Audio.\",\n \"reference_id\": \"model-id\",\n \"temperature\": 0.7,\n \"top_p\": 0.7,\n \"prosody\": {\n \"speed\": 1,\n \"volume\": 0,\n \"normalize_loudness\": true\n },\n \"chunk_length\": 300,\n \"normalize\": true,\n \"format\": \"mp3\",\n \"sample_rate\": 44100,\n \"mp3_bitrate\": 128,\n \"latency\": \"normal\",\n \"max_new_tokens\": 1024,\n \"repetition_penalty\": 1.2,\n \"min_chunk_length\": 50,\n \"condition_on_previous_chunks\": true,\n \"early_stop_threshold\": 1\n }'" - }, - { - "lang": "bash", - "label": "Multi Speaker (S2 family only)", - "source": "curl --request POST \\\n --url https://api.fish.audio/v1/tts \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: s2.1-pro-free' \\\n --data '{\n \"text\": \"<|speaker:0|>Hello!<|speaker:1|>Hi there!\",\n \"reference_id\": [\"speaker-a-id\", \"speaker-b-id\"],\n \"temperature\": 0.7,\n \"top_p\": 0.7,\n \"prosody\": {\n \"speed\": 1,\n \"volume\": 0,\n \"normalize_loudness\": true\n },\n \"chunk_length\": 300,\n \"normalize\": true,\n \"format\": \"mp3\",\n \"sample_rate\": 44100,\n \"mp3_bitrate\": 128,\n \"latency\": \"normal\",\n \"max_new_tokens\": 1024,\n \"repetition_penalty\": 1.2,\n \"min_chunk_length\": 50,\n \"condition_on_previous_chunks\": true,\n \"early_stop_threshold\": 1\n }'" - } + "Agent Tests" ] } }, - "/v1/tts/stream/with-timestamp": { - "post": { - "summary": "Text to Speech Stream with Timestamps", + "/v1/agent/test-runs/{run_id}": { + "get": { + "summary": "Get Test Run", + "description": "Fetch one test run. Runs start `queued` and end `passed`, `failed` or\n`error`. A simulation run takes minutes, so poll until it finishes.", "security": [ { "BearerAuth": [] @@ -10864,318 +11408,3302 @@ ], "parameters": [ { - "in": "header", - "name": "model", - "description": "Specify which TTS model to use. Use `s2.1-pro-free` for the free developer tier. `drama-3-preview` is a preview model; its behavior and availability may change. If omitted or set to an unrecognized value, the request falls back to `s2.1-pro`.", - "required": false, + "in": "path", + "name": "run_id", + "description": "", + "required": true, "schema": { - "default": "s2.1-pro", - "enum": [ - "s1", - "s2-pro", - "s2.1-pro", - "s2.1-pro-free", - "drama-3-preview" - ], - "title": "Model", + "title": "Run Id", "type": "string" }, "deprecated": false } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TTSStreamWithTimestampRequest" + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "run_id": { + "title": "Run Id", + "type": "string" + }, + "test_id": { + "title": "Test Id", + "type": "string" + }, + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "batch_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Batch Id" + }, + "test_type": { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "title": "Test Type", + "type": "string" + }, + "repeat_index": { + "default": 0, + "description": "Which repeat of the test in its batch this run is.", + "title": "Repeat Index", + "type": "integer" + }, + "status": { + "enum": [ + "queued", + "running", + "passed", + "failed", + "error" + ], + "title": "Status", + "type": "string" + }, + "needs_review": { + "default": false, + "description": "The judge could not decide, or a tool call had no mock answer.", + "title": "Needs Review", + "type": "boolean" + }, + "error": { + "default": "", + "title": "Error", + "type": "string" + }, + "usage": { + "$ref": "#/components/schemas/PublicAgentTestRunUsage" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "started_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Started At" + }, + "finished_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Finished At" + }, + "agent_reply": { + "default": "", + "description": "The reply a scripted test judged.", + "title": "Agent Reply", + "type": "string" + }, + "judge_reasoning": { + "default": "", + "title": "Judge Reasoning", + "type": "string" + }, + "config_hash": { + "default": "", + "description": "The agent draft's config hash the run tested.", + "title": "Config Hash", + "type": "string" + }, + "latency_ms": { + "default": 0, + "title": "Latency Ms", + "type": "integer" + }, + "simulation_result": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentTestSimulationResult" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "enum": [ + "web_voice", + "phone_inbound", + "phone_outbound" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Channel" + }, + "turns_used": { + "default": 0, + "title": "Turns Used", + "type": "integer" + }, + "ended_by": { + "anyOf": [ + { + "enum": [ + "agent", + "user", + "transfer", + "max_turns", + "timeout", + "error" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ended By" + }, + "mock_gaps": { + "items": { + "$ref": "#/components/schemas/AgentTestMockGap" + }, + "title": "Mock Gaps", + "type": "array" + } + }, + "required": [ + "run_id", + "test_id", + "agent_id", + "test_type", + "status", + "created_at" + ], + "type": "object" + } } - }, - "application/msgpack": { - "schema": { - "$ref": "#/components/schemas/TTSStreamWithTimestampRequest" + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } } } - } - }, - "responses": { - "200": { - "description": "Server-Sent Events stream. Each `message` event contains a JSON payload with one base64 audio chunk. Concatenate every `audio_base64` chunk in arrival order to reconstruct the complete audio. `alignment` is the latest cumulative timestamp snapshot for `chunk_seq`; clients should replace the previous snapshot for that chunk instead of appending segments. `chunk_audio_offset_sec` can be added to segment times to derive absolute timestamps in the full audio.", - "headers": { - "Transfer-Encoding": { + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { "schema": { - "type": "string" - }, - "description": "chunked" + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } } - }, + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, "content": { - "text/event-stream": { + "application/json": { "schema": { - "description": "One Server-Sent Events message payload for streaming TTS with timestamps. Each event contains one audio chunk. Concatenate all `audio_base64` chunks in arrival order to reconstruct the complete audio. `alignment` is the latest cumulative timestamp snapshot for the reported `chunk_seq`; clients should replace the previous snapshot for that chunk instead of appending segments.", - "examples": [ - { - "alignment": { - "audio_duration": 16.24, - "segments": [ - { - "end": 0.16, - "start": 0, - "text": "I" - }, - { - "end": 0.48, - "start": 0.16, - "text": "can't" - }, - { - "end": 0.8, - "start": 0.48, - "text": "believe" - }, - { - "end": 1.12, - "start": 0.8, - "text": "its" - }, - { - "end": 1.44, - "start": 1.2, - "text": "been" - }, - { - "end": 1.76, - "start": 1.44, - "text": "this" - }, - { - "end": 2.48, - "start": 1.76, - "text": "long" - }, - { - "end": 2.64, - "start": 2.56, - "text": "It" - }, - { - "end": 3.04, - "start": 2.72, - "text": "feels" - }, - { - "end": 3.28, - "start": 3.12, - "text": "like" - }, - { - "end": 4, - "start": 3.36, - "text": "forever" - }, - { - "end": 4.32, - "start": 4, - "text": "since" - }, - { - "end": 4.48, - "start": 4.32, - "text": "we" - }, - { - "end": 4.96, - "start": 4.48, - "text": "last" - }, - { - "end": 5.28, - "start": 4.96, - "text": "really" - }, - { - "end": 5.84, - "start": 5.28, - "text": "talked" - }, - { - "end": 6.24, - "start": 6, - "text": "Ive" - }, - { - "end": 6.64, - "start": 6.24, - "text": "missed" - }, - { - "end": 6.96, - "start": 6.64, - "text": "hearing" - }, - { - "end": 7.2, - "start": 6.96, - "text": "your" - }, - { - "end": 7.76, - "start": 7.2, - "text": "voice" - }, - { - "end": 7.92, - "start": 7.76, - "text": "your" - }, - { - "end": 8.48, - "start": 7.92, - "text": "stories" - }, - { - "end": 8.72, - "start": 8.48, - "text": "even" - }, - { - "end": 8.8, - "start": 8.72, - "text": "the" - }, - { - "end": 9.2, - "start": 8.8, - "text": "little" - }, - { - "end": 9.52, - "start": 9.2, - "text": "things" - }, - { - "end": 9.68, - "start": 9.52, - "text": "you" - }, - { - "end": 10, - "start": 9.68, - "text": "used" - }, - { - "end": 10.08, - "start": 10, - "text": "to" - }, - { - "end": 10.64, - "start": 10.08, - "text": "say" - }, - { - "end": 10.96, - "start": 10.64, - "text": "How" - }, - { - "end": 11.12, - "start": 10.96, - "text": "have" - }, - { - "end": 11.36, - "start": 11.12, - "text": "you" - }, - { - "end": 11.92, - "start": 11.36, - "text": "been" - }, - { - "end": 12.24, - "start": 12, - "text": "Ive" - }, - { - "end": 12.48, - "start": 12.24, - "text": "thought" - }, - { - "end": 12.8, - "start": 12.48, - "text": "about" - }, - { - "end": 13.2, - "start": 12.8, - "text": "calling" - }, - { - "end": 13.36, - "start": 13.2, - "text": "you" - }, - { - "end": 13.68, - "start": 13.36, - "text": "so" - }, - { - "end": 13.92, - "start": 13.68, - "text": "many" - }, - { - "end": 14.56, - "start": 13.92, - "text": "times" - }, - { - "end": 14.72, - "start": 14.56, - "text": "but" - }, - { - "end": 14.88, - "start": 14.72, - "text": "I" - }, - { - "end": 15.2, - "start": 14.88, - "text": "never" - }, - { - "end": 15.36, - "start": 15.2, - "text": "knew" - }, - { - "end": 15.6, - "start": 15.36, - "text": "where" - }, - { - "end": 15.6, - "start": 15.6, - "text": "to" - }, - { - "end": 16.24, - "start": 15.68, - "text": "start" - } - ] - }, - "audio_base64": "SUQzBAAAAAAA...", - "chunk_audio_offset_sec": 0, - "chunk_seq": 0, - "content": "I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start." + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - { + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Agent Tests" + ] + } + }, + "/v1/agent/phone-numbers": { + "get": { + "summary": "List Phone Numbers", + "description": "List your team's phone numbers, newest first. Released numbers are gone\nfor good and never appear. Look an id up by E.164 with `phone_number`, or\nfilter with `agent_id` to see one agent's numbers. Paginate with `cursor`\n(follow `next_cursor` while `has_more` is true) or with `page` for offset\npagination with a `total` count — the two are mutually exclusive.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "phone_number", + "description": "Exact E.164 match, e.g. +14155550123.", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Phone Number" + }, + "deprecated": false + }, + { + "in": "query", + "name": "agent_id", + "description": "Only numbers bound to this agent.", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Id" + }, + "deprecated": false + }, + { + "in": "query", + "name": "cursor", + "description": "", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cursor" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page", + "description": "1-based page number; mutually exclusive with cursor.", + "required": false, + "schema": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Page" + }, + "deprecated": false + }, + { + "in": "query", + "name": "include_total", + "description": "", + "required": false, + "schema": { + "default": false, + "title": "Include Total", + "type": "boolean" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page_size", + "description": "", + "required": false, + "schema": { + "default": 30, + "maximum": 100, + "minimum": 1, + "title": "Page Size", + "type": "integer" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "phone_numbers": { + "items": { + "$ref": "#/components/schemas/PublicPhoneNumberEntity" + }, + "title": "Phone Numbers", + "type": "array" + }, + "has_more": { + "title": "Has More", + "type": "boolean" + }, + "next_cursor": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Next Cursor" + }, + "total": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Total" + } + }, + "required": [ + "phone_numbers", + "has_more" + ], + "type": "object" + } + } + } + }, + "400": { + "description": "Bad request syntax or unsupported method", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Phone Numbers" + ] + }, + "post": { + "summary": "Purchase or Import Phone Number", + "description": "`provider` discriminates two variants. `twilio` buys a number from the\nmanaged inventory: it lands in your default workspace, any `agent_id` you\nbind must live there too, and billing is the monthly price charged in\ndaily slices; the first day is charged before anything is bought (402\ncosts you nothing) and the daily run advances it from there. `sip`\nimports a number you already own at your carrier: point your trunk's\norigination at our SIP host, provide inbound authentication (digest\nand/or source CIDRs) and optionally a termination host so the number can\nplace calls; nothing is rented and there is no monthly fee. Either way,\n409 means the number is already on the platform; 502 means the provider\nor trunk provisioning refused, and the number stays visible with status\n`error` and is safe to release.", + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/PublicPhoneNumberPurchasePayload" + }, + { + "$ref": "#/components/schemas/PublicSipNumberImportPayload" + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "Document created, URL follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "phone_number_id": { + "title": "Phone Number Id", + "type": "string" + }, + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "phone_number": { + "description": "E.164, e.g. +14155550123.", + "title": "Phone Number", + "type": "string" + }, + "provider": { + "enum": [ + "livekit", + "twilio", + "sip" + ], + "title": "Provider", + "type": "string" + }, + "label": { + "default": "", + "title": "Label", + "type": "string" + }, + "agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Inbound calls route to this agent; unbound numbers ring busy.", + "title": "Agent Id" + }, + "cold_transfer_use_original_caller": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Managed `twilio` numbers: whether a transfer target, cold or warm, sees the original caller's number (true) or this number (false). `null` for imported `sip` numbers, whose carrier owns the setting.", + "title": "Cold Transfer Use Original Caller" + }, + "cold_transfer_use_original_caller_effective": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The cold-transfer policy the carrier has confirmed; `null` while unknown or for imported `sip` numbers. Warm transfers follow `cold_transfer_use_original_caller` directly from the next call.", + "title": "Cold Transfer Use Original Caller Effective" + }, + "caller_id_sync_status": { + "default": "carrier_managed", + "description": "`synced` once the carrier confirmed the policy, `pending` while it is being applied, `error` when the last attempt failed (send `retry_caller_id_sync`), `carrier_managed` for imported `sip` numbers.", + "enum": [ + "synced", + "pending", + "error", + "carrier_managed" + ], + "title": "Caller Id Sync Status", + "type": "string" + }, + "caller_id_sync_error": { + "default": "", + "description": "Why the last synchronization failed; empty otherwise.", + "title": "Caller Id Sync Error", + "type": "string" + }, + "status": { + "enum": [ + "provisioning", + "active", + "error", + "released" + ], + "title": "Status", + "type": "string" + }, + "status_detail": { + "default": "", + "description": "What failed when status is `error`; empty otherwise.", + "title": "Status Detail", + "type": "string" + }, + "supports_outbound": { + "default": false, + "description": "Whether the number can place calls (outbound and warm-transfer consult legs): managed `twilio` always, imported `sip` only when a termination was configured.", + "title": "Supports Outbound", + "type": "boolean" + }, + "termination_uri": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the customer trunk's termination host.", + "title": "Termination Uri" + }, + "inbound_auth_username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the inbound digest username. Passwords are never echoed.", + "title": "Inbound Auth Username" + }, + "inbound_allowed_addresses": { + "description": "Imported `sip` numbers: allowed source IPs/CIDRs.", + "items": { + "type": "string" + }, + "title": "Inbound Allowed Addresses", + "type": "array" + }, + "termination_transport": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: termination transport.", + "title": "Termination Transport" + }, + "termination_auth_username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the termination digest username. Passwords are never echoed.", + "title": "Termination Auth Username" + }, + "termination_headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Imported `sip` numbers: custom SIP headers added to every outbound INVITE through the termination.", + "title": "Termination Headers", + "type": "object" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + } + }, + "required": [ + "phone_number_id", + "workspace_id", + "phone_number", + "provider", + "agent_id", + "status", + "created_at", + "updated_at" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "402": { + "description": "No payment -- see charging schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "403": { + "description": "Request forbidden -- authorization will not help", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "409": { + "description": "Request conflict", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "422": { + "description": "", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "502": { + "description": "Invalid responses from another server/proxy", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Phone Numbers" + ] + } + }, + "/v1/agent/phone-numbers/{phone_number_id}": { + "get": { + "summary": "Get Phone Number", + "description": "Fetch one phone number, including its current agent binding and\nprovisioning status.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "phone_number_id", + "description": "", + "required": true, + "schema": { + "title": "Phone Number Id", + "type": "string" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "phone_number_id": { + "title": "Phone Number Id", + "type": "string" + }, + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "phone_number": { + "description": "E.164, e.g. +14155550123.", + "title": "Phone Number", + "type": "string" + }, + "provider": { + "enum": [ + "livekit", + "twilio", + "sip" + ], + "title": "Provider", + "type": "string" + }, + "label": { + "default": "", + "title": "Label", + "type": "string" + }, + "agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Inbound calls route to this agent; unbound numbers ring busy.", + "title": "Agent Id" + }, + "cold_transfer_use_original_caller": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Managed `twilio` numbers: whether a transfer target, cold or warm, sees the original caller's number (true) or this number (false). `null` for imported `sip` numbers, whose carrier owns the setting.", + "title": "Cold Transfer Use Original Caller" + }, + "cold_transfer_use_original_caller_effective": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The cold-transfer policy the carrier has confirmed; `null` while unknown or for imported `sip` numbers. Warm transfers follow `cold_transfer_use_original_caller` directly from the next call.", + "title": "Cold Transfer Use Original Caller Effective" + }, + "caller_id_sync_status": { + "default": "carrier_managed", + "description": "`synced` once the carrier confirmed the policy, `pending` while it is being applied, `error` when the last attempt failed (send `retry_caller_id_sync`), `carrier_managed` for imported `sip` numbers.", + "enum": [ + "synced", + "pending", + "error", + "carrier_managed" + ], + "title": "Caller Id Sync Status", + "type": "string" + }, + "caller_id_sync_error": { + "default": "", + "description": "Why the last synchronization failed; empty otherwise.", + "title": "Caller Id Sync Error", + "type": "string" + }, + "status": { + "enum": [ + "provisioning", + "active", + "error", + "released" + ], + "title": "Status", + "type": "string" + }, + "status_detail": { + "default": "", + "description": "What failed when status is `error`; empty otherwise.", + "title": "Status Detail", + "type": "string" + }, + "supports_outbound": { + "default": false, + "description": "Whether the number can place calls (outbound and warm-transfer consult legs): managed `twilio` always, imported `sip` only when a termination was configured.", + "title": "Supports Outbound", + "type": "boolean" + }, + "termination_uri": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the customer trunk's termination host.", + "title": "Termination Uri" + }, + "inbound_auth_username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the inbound digest username. Passwords are never echoed.", + "title": "Inbound Auth Username" + }, + "inbound_allowed_addresses": { + "description": "Imported `sip` numbers: allowed source IPs/CIDRs.", + "items": { + "type": "string" + }, + "title": "Inbound Allowed Addresses", + "type": "array" + }, + "termination_transport": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: termination transport.", + "title": "Termination Transport" + }, + "termination_auth_username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the termination digest username. Passwords are never echoed.", + "title": "Termination Auth Username" + }, + "termination_headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Imported `sip` numbers: custom SIP headers added to every outbound INVITE through the termination.", + "title": "Termination Headers", + "type": "object" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + } + }, + "required": [ + "phone_number_id", + "workspace_id", + "phone_number", + "provider", + "agent_id", + "status", + "created_at", + "updated_at" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Phone Numbers" + ] + }, + "patch": { + "summary": "Update Phone Number", + "description": "Change the label and/or repoint the number at another agent — the\ndeployment-pipeline move (rebind from the staging agent to the production\none). Send `agent_id: null` to unbind; unbound numbers ring busy. The\nagent must live in the number's workspace. Rebinding is a routing-table\nupdate resolved on the next inbound call; nothing about the number itself\nis reprovisioned.\n\nManaged `twilio` numbers also accept `cold_transfer_use_original_caller`,\nwhich picks the number a transfer target sees (cold and warm alike), and\n`retry_caller_id_sync` to re-apply it after a failed synchronization; read\n`caller_id_sync_status` on the response. Imported `sip` numbers return 409\nfor either field because their carrier owns the setting.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "phone_number_id", + "description": "", + "required": true, + "schema": { + "title": "Phone Number Id", + "type": "string" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicPhoneNumberUpdatePayload" + } + } + } + }, + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "phone_number_id": { + "title": "Phone Number Id", + "type": "string" + }, + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "phone_number": { + "description": "E.164, e.g. +14155550123.", + "title": "Phone Number", + "type": "string" + }, + "provider": { + "enum": [ + "livekit", + "twilio", + "sip" + ], + "title": "Provider", + "type": "string" + }, + "label": { + "default": "", + "title": "Label", + "type": "string" + }, + "agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Inbound calls route to this agent; unbound numbers ring busy.", + "title": "Agent Id" + }, + "cold_transfer_use_original_caller": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Managed `twilio` numbers: whether a transfer target, cold or warm, sees the original caller's number (true) or this number (false). `null` for imported `sip` numbers, whose carrier owns the setting.", + "title": "Cold Transfer Use Original Caller" + }, + "cold_transfer_use_original_caller_effective": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The cold-transfer policy the carrier has confirmed; `null` while unknown or for imported `sip` numbers. Warm transfers follow `cold_transfer_use_original_caller` directly from the next call.", + "title": "Cold Transfer Use Original Caller Effective" + }, + "caller_id_sync_status": { + "default": "carrier_managed", + "description": "`synced` once the carrier confirmed the policy, `pending` while it is being applied, `error` when the last attempt failed (send `retry_caller_id_sync`), `carrier_managed` for imported `sip` numbers.", + "enum": [ + "synced", + "pending", + "error", + "carrier_managed" + ], + "title": "Caller Id Sync Status", + "type": "string" + }, + "caller_id_sync_error": { + "default": "", + "description": "Why the last synchronization failed; empty otherwise.", + "title": "Caller Id Sync Error", + "type": "string" + }, + "status": { + "enum": [ + "provisioning", + "active", + "error", + "released" + ], + "title": "Status", + "type": "string" + }, + "status_detail": { + "default": "", + "description": "What failed when status is `error`; empty otherwise.", + "title": "Status Detail", + "type": "string" + }, + "supports_outbound": { + "default": false, + "description": "Whether the number can place calls (outbound and warm-transfer consult legs): managed `twilio` always, imported `sip` only when a termination was configured.", + "title": "Supports Outbound", + "type": "boolean" + }, + "termination_uri": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the customer trunk's termination host.", + "title": "Termination Uri" + }, + "inbound_auth_username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the inbound digest username. Passwords are never echoed.", + "title": "Inbound Auth Username" + }, + "inbound_allowed_addresses": { + "description": "Imported `sip` numbers: allowed source IPs/CIDRs.", + "items": { + "type": "string" + }, + "title": "Inbound Allowed Addresses", + "type": "array" + }, + "termination_transport": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: termination transport.", + "title": "Termination Transport" + }, + "termination_auth_username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Imported `sip` numbers: the termination digest username. Passwords are never echoed.", + "title": "Termination Auth Username" + }, + "termination_headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Imported `sip` numbers: custom SIP headers added to every outbound INVITE through the termination.", + "title": "Termination Headers", + "type": "object" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + } + }, + "required": [ + "phone_number_id", + "workspace_id", + "phone_number", + "provider", + "agent_id", + "status", + "created_at", + "updated_at" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "409": { + "description": "Request conflict", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "422": { + "description": "", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Phone Numbers" + ] + }, + "delete": { + "summary": "Release Phone Number", + "description": "Release a number back to the provider's inventory and stop its daily\nbilling. This is irreversible: anyone — including other platforms — can\nbuy the number afterwards, so callers who saved it may reach a stranger.\nThe number disappears from this API immediately.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "phone_number_id", + "description": "", + "required": true, + "schema": { + "title": "Phone Number Id", + "type": "string" + }, + "deprecated": false + } + ], + "responses": { + "204": { + "description": "Request fulfilled, nothing follows", + "headers": {} + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Phone Numbers" + ] + } + }, + "/v1/agent/phone-calls": { + "post": { + "summary": "Create Phone Call", + "description": "Place an outbound call from one of your phone numbers. Platform numbers\ndial US, Canada or Japan destinations, and a domestic trunk 0 after +81\n(e.g. +81080...) is accepted and normalized to E.164 (+8180...).\nImported SIP numbers dial through their own termination and may attach\ncustom INVITE headers with `sip_headers`. Returns immediately with the\nsession queued for dialing. Subscribe to the `phone_call.dial_finished`\nwebhook or poll `GET /v1/agent/sessions/{session_id}` for the dial\noutcome and the leg's `sip_call_id`. Ringing is never billed: metering\nstarts when the callee answers.\n\nErrors carry a machine-readable `reason` (e.g. `destination_not_allowed`,\n`insufficient_credit`, `concurrency_limit_exceeded`).", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "header", + "name": "idempotency-key", + "description": "Retry-safe replay key: the same key with the same body returns the call already placed instead of dialing again (24h window).", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Idempotency-Key" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PhoneCallCreatePayload" + } + } + } + }, + "responses": { + "201": { + "description": "Document created, URL follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "session_id": { + "title": "Session Id", + "type": "string" + }, + "status": { + "const": "queued", + "default": "queued", + "title": "Status", + "type": "string" + } + }, + "required": [ + "session_id" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "402": { + "description": "No payment -- see charging schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "403": { + "description": "Request forbidden -- authorization will not help", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "409": { + "description": "Request conflict", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "422": { + "description": "", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "429": { + "description": "The user has sent too many requests in a given amount of time (\"rate limiting\")", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "502": { + "description": "Invalid responses from another server/proxy", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Phone Calls" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Create Phone Call", + "source": "curl --request POST \\\n --url https://api.fish.audio/v1/agent/phone-calls \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'Idempotency-Key: ' \\\n --data '{\n \"agent_id\": \"\",\n \"phone_number_id\": \"\",\n \"to_number\": \"+14155550123\"\n }'" + } + ] + } + }, + "/v1/asr": { + "post": { + "summary": "Speech to Text", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "header", + "name": "model", + "description": "Specify which speech-to-text model to use.", + "required": false, + "schema": { + "default": "transcribe-1", + "enum": [ + "transcribe-1", + "transcribe-1-pro" + ], + "title": "Model", + "type": "string" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "properties": { + "audio": { + "description": "Audio file to be converted to text", + "format": "binary", + "title": "Audio", + "type": "string" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional hint. The language is auto-detected regardless; the detected language is returned as `language_code`.", + "title": "Language" + }, + "ignore_timestamps": { + "default": true, + "description": "Whether to return precise timestamps in the text, this will increase the latency in audio shorter than 30 seconds", + "title": "Ignore Timestamps", + "type": "boolean" + } + }, + "required": [ + "audio" + ], + "type": "object" + } + }, + "application/msgpack": { + "schema": { + "properties": { + "audio": { + "description": "Audio file to be converted to text", + "format": "binary", + "title": "Audio", + "type": "string" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional hint. The language is auto-detected regardless; the detected language is returned as `language_code`.", + "title": "Language" + }, + "ignore_timestamps": { + "default": true, + "description": "Whether to return precise timestamps in the text, this will increase the latency in audio shorter than 30 seconds", + "title": "Ignore Timestamps", + "type": "boolean" + } + }, + "required": [ + "audio" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "text": { + "title": "Text", + "type": "string" + }, + "duration": { + "description": "Duration of the audio in seconds", + "title": "Duration", + "type": "number" + }, + "segments": { + "items": { + "$ref": "#/components/schemas/ASRSegment" + }, + "title": "Segments", + "type": "array" + }, + "language_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Detected language as an ISO 639-1 code (e.g. `en`, `ja`). Omitted if no language is detected.", + "title": "Language Code" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Detected language name (e.g. `English`). For display only; use `language_code` in code.", + "title": "Language" + } + }, + "required": [ + "text", + "duration", + "segments" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "402": { + "description": "No payment -- see charging schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "OpenAPI v1" + ] + } + }, + "/v1/tts": { + "post": { + "summary": "Text to Speech", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "header", + "name": "model", + "description": "Specify which TTS model to use. Use `s2.1-pro-free` for the free developer tier. `drama-3-preview` is a preview model; its behavior and availability may change. If omitted or set to an unrecognized value, the request falls back to `s2.1-pro`.", + "required": false, + "schema": { + "default": "s2.1-pro", + "enum": [ + "s1", + "s2-pro", + "s2.1-pro", + "s2.1-pro-free", + "drama-3-preview" + ], + "title": "Model", + "type": "string" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TTSRequest" + } + }, + "application/msgpack": { + "schema": { + "$ref": "#/components/schemas/TTSRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": { + "Transfer-Encoding": { + "schema": { + "type": "string" + }, + "description": "chunked" + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "402": { + "description": "No payment -- see charging schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "OpenAPI v1" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Single Speaker", + "source": "curl --request POST \\\n --url https://api.fish.audio/v1/tts \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: s2.1-pro-free' \\\n --data '{\n \"text\": \"Hello! Welcome to Fish Audio.\",\n \"reference_id\": \"model-id\",\n \"temperature\": 0.7,\n \"top_p\": 0.7,\n \"prosody\": {\n \"speed\": 1,\n \"volume\": 0,\n \"normalize_loudness\": true\n },\n \"chunk_length\": 300,\n \"normalize\": true,\n \"format\": \"mp3\",\n \"sample_rate\": 44100,\n \"mp3_bitrate\": 128,\n \"latency\": \"normal\",\n \"max_new_tokens\": 1024,\n \"repetition_penalty\": 1.2,\n \"min_chunk_length\": 50,\n \"condition_on_previous_chunks\": true,\n \"early_stop_threshold\": 1\n }'" + }, + { + "lang": "bash", + "label": "Multi Speaker (S2 family only)", + "source": "curl --request POST \\\n --url https://api.fish.audio/v1/tts \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: s2.1-pro-free' \\\n --data '{\n \"text\": \"<|speaker:0|>Hello!<|speaker:1|>Hi there!\",\n \"reference_id\": [\"speaker-a-id\", \"speaker-b-id\"],\n \"temperature\": 0.7,\n \"top_p\": 0.7,\n \"prosody\": {\n \"speed\": 1,\n \"volume\": 0,\n \"normalize_loudness\": true\n },\n \"chunk_length\": 300,\n \"normalize\": true,\n \"format\": \"mp3\",\n \"sample_rate\": 44100,\n \"mp3_bitrate\": 128,\n \"latency\": \"normal\",\n \"max_new_tokens\": 1024,\n \"repetition_penalty\": 1.2,\n \"min_chunk_length\": 50,\n \"condition_on_previous_chunks\": true,\n \"early_stop_threshold\": 1\n }'" + } + ] + } + }, + "/v1/tts/stream/with-timestamp": { + "post": { + "summary": "Text to Speech Stream with Timestamps", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "header", + "name": "model", + "description": "Specify which TTS model to use. Use `s2.1-pro-free` for the free developer tier. `drama-3-preview` is a preview model; its behavior and availability may change. If omitted or set to an unrecognized value, the request falls back to `s2.1-pro`.", + "required": false, + "schema": { + "default": "s2.1-pro", + "enum": [ + "s1", + "s2-pro", + "s2.1-pro", + "s2.1-pro-free", + "drama-3-preview" + ], + "title": "Model", + "type": "string" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TTSStreamWithTimestampRequest" + } + }, + "application/msgpack": { + "schema": { + "$ref": "#/components/schemas/TTSStreamWithTimestampRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Server-Sent Events stream. Each `message` event contains a JSON payload with one base64 audio chunk. Concatenate every `audio_base64` chunk in arrival order to reconstruct the complete audio. `alignment` is the latest cumulative timestamp snapshot for `chunk_seq`; clients should replace the previous snapshot for that chunk instead of appending segments. `chunk_audio_offset_sec` can be added to segment times to derive absolute timestamps in the full audio.", + "headers": { + "Transfer-Encoding": { + "schema": { + "type": "string" + }, + "description": "chunked" + } + }, + "content": { + "text/event-stream": { + "schema": { + "description": "One Server-Sent Events message payload for streaming TTS with timestamps. Each event contains one audio chunk. Concatenate all `audio_base64` chunks in arrival order to reconstruct the complete audio. `alignment` is the latest cumulative timestamp snapshot for the reported `chunk_seq`; clients should replace the previous snapshot for that chunk instead of appending segments.", + "examples": [ + { + "alignment": { + "audio_duration": 16.24, + "segments": [ + { + "end": 0.16, + "start": 0, + "text": "I" + }, + { + "end": 0.48, + "start": 0.16, + "text": "can't" + }, + { + "end": 0.8, + "start": 0.48, + "text": "believe" + }, + { + "end": 1.12, + "start": 0.8, + "text": "its" + }, + { + "end": 1.44, + "start": 1.2, + "text": "been" + }, + { + "end": 1.76, + "start": 1.44, + "text": "this" + }, + { + "end": 2.48, + "start": 1.76, + "text": "long" + }, + { + "end": 2.64, + "start": 2.56, + "text": "It" + }, + { + "end": 3.04, + "start": 2.72, + "text": "feels" + }, + { + "end": 3.28, + "start": 3.12, + "text": "like" + }, + { + "end": 4, + "start": 3.36, + "text": "forever" + }, + { + "end": 4.32, + "start": 4, + "text": "since" + }, + { + "end": 4.48, + "start": 4.32, + "text": "we" + }, + { + "end": 4.96, + "start": 4.48, + "text": "last" + }, + { + "end": 5.28, + "start": 4.96, + "text": "really" + }, + { + "end": 5.84, + "start": 5.28, + "text": "talked" + }, + { + "end": 6.24, + "start": 6, + "text": "Ive" + }, + { + "end": 6.64, + "start": 6.24, + "text": "missed" + }, + { + "end": 6.96, + "start": 6.64, + "text": "hearing" + }, + { + "end": 7.2, + "start": 6.96, + "text": "your" + }, + { + "end": 7.76, + "start": 7.2, + "text": "voice" + }, + { + "end": 7.92, + "start": 7.76, + "text": "your" + }, + { + "end": 8.48, + "start": 7.92, + "text": "stories" + }, + { + "end": 8.72, + "start": 8.48, + "text": "even" + }, + { + "end": 8.8, + "start": 8.72, + "text": "the" + }, + { + "end": 9.2, + "start": 8.8, + "text": "little" + }, + { + "end": 9.52, + "start": 9.2, + "text": "things" + }, + { + "end": 9.68, + "start": 9.52, + "text": "you" + }, + { + "end": 10, + "start": 9.68, + "text": "used" + }, + { + "end": 10.08, + "start": 10, + "text": "to" + }, + { + "end": 10.64, + "start": 10.08, + "text": "say" + }, + { + "end": 10.96, + "start": 10.64, + "text": "How" + }, + { + "end": 11.12, + "start": 10.96, + "text": "have" + }, + { + "end": 11.36, + "start": 11.12, + "text": "you" + }, + { + "end": 11.92, + "start": 11.36, + "text": "been" + }, + { + "end": 12.24, + "start": 12, + "text": "Ive" + }, + { + "end": 12.48, + "start": 12.24, + "text": "thought" + }, + { + "end": 12.8, + "start": 12.48, + "text": "about" + }, + { + "end": 13.2, + "start": 12.8, + "text": "calling" + }, + { + "end": 13.36, + "start": 13.2, + "text": "you" + }, + { + "end": 13.68, + "start": 13.36, + "text": "so" + }, + { + "end": 13.92, + "start": 13.68, + "text": "many" + }, + { + "end": 14.56, + "start": 13.92, + "text": "times" + }, + { + "end": 14.72, + "start": 14.56, + "text": "but" + }, + { + "end": 14.88, + "start": 14.72, + "text": "I" + }, + { + "end": 15.2, + "start": 14.88, + "text": "never" + }, + { + "end": 15.36, + "start": 15.2, + "text": "knew" + }, + { + "end": 15.6, + "start": 15.36, + "text": "where" + }, + { + "end": 15.6, + "start": 15.6, + "text": "to" + }, + { + "end": 16.24, + "start": 15.68, + "text": "start" + } + ] + }, + "audio_base64": "SUQzBAAAAAAA...", + "chunk_audio_offset_sec": 0, + "chunk_seq": 0, + "content": "I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start." + }, + { "alignment": { "audio_duration": 16.24, "segments": [ @@ -11415,519 +14943,1828 @@ "text": "knew" }, { - "end": 15.6, - "start": 15.36, - "text": "where" + "end": 15.6, + "start": 15.36, + "text": "where" + }, + { + "end": 15.6, + "start": 15.6, + "text": "to" + }, + { + "end": 16.24, + "start": 15.68, + "text": "start" + } + ] + }, + "audio_base64": "//uSxOAAF...", + "chunk_audio_offset_sec": 0, + "chunk_seq": 0, + "content": "I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start." + }, + { + "alignment": { + "audio_duration": 10.48, + "segments": [ + { + "end": 0.8, + "start": 0.4, + "text": "Seeing" + }, + { + "end": 0.96, + "start": 0.8, + "text": "you" + }, + { + "end": 1.44, + "start": 0.96, + "text": "again" + }, + { + "end": 1.68, + "start": 1.44, + "text": "now" + }, + { + "end": 2.08, + "start": 1.68, + "text": "makes" + }, + { + "end": 2.24, + "start": 2.08, + "text": "me" + }, + { + "end": 2.8, + "start": 2.24, + "text": "realize" + }, + { + "end": 3.12, + "start": 2.8, + "text": "just" + }, + { + "end": 3.28, + "start": 3.12, + "text": "how" + }, + { + "end": 3.6, + "start": 3.28, + "text": "much" + }, + { + "end": 3.76, + "start": 3.6, + "text": "Ive" + }, + { + "end": 4.24, + "start": 3.84, + "text": "missed" + }, + { + "end": 4.56, + "start": 4.24, + "text": "you" + }, + { + "end": 4.8, + "start": 4.64, + "text": "We" + }, + { + "end": 5.04, + "start": 4.8, + "text": "have" + }, + { + "end": 5.36, + "start": 5.04, + "text": "so" + }, + { + "end": 5.76, + "start": 5.36, + "text": "much" + }, + { + "end": 5.76, + "start": 5.76, + "text": "to" + }, + { + "end": 6.16, + "start": 5.76, + "text": "catch" + }, + { + "end": 6.4, + "start": 6.16, + "text": "up" + }, + { + "end": 6.72, + "start": 6.4, + "text": "on" + }, + { + "end": 6.96, + "start": 6.8, + "text": "and" + }, + { + "end": 7.04, + "start": 6.96, + "text": "I" + }, + { + "end": 7.36, + "start": 7.04, + "text": "dont" + }, + { + "end": 7.6, + "start": 7.36, + "text": "even" + }, + { + "end": 7.84, + "start": 7.6, + "text": "know" + }, + { + "end": 8.08, + "start": 7.84, + "text": "which" + }, + { + "end": 8.4, + "start": 8.08, + "text": "part" + }, + { + "end": 8.48, + "start": 8.4, + "text": "of" + }, + { + "end": 8.72, + "start": 8.56, + "text": "my" + }, + { + "end": 8.96, + "start": 8.72, + "text": "life" + }, + { + "end": 9.12, + "start": 9.12, + "text": "to" + }, + { + "end": 9.44, + "start": 9.12, + "text": "tell" + }, + { + "end": 9.6, + "start": 9.44, + "text": "you" }, { - "end": 15.6, - "start": 15.6, - "text": "to" + "end": 10, + "start": 9.6, + "text": "about" }, { - "end": 16.24, - "start": 15.68, - "text": "start" + "end": 10.48, + "start": 10.08, + "text": "first" } ] }, - "audio_base64": "//uSxOAAF...", - "chunk_audio_offset_sec": 0, - "chunk_seq": 0, - "content": "I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start." - }, - { - "alignment": { - "audio_duration": 10.48, - "segments": [ - { - "end": 0.8, - "start": 0.4, - "text": "Seeing" - }, - { - "end": 0.96, - "start": 0.8, - "text": "you" - }, - { - "end": 1.44, - "start": 0.96, - "text": "again" - }, - { - "end": 1.68, - "start": 1.44, - "text": "now" - }, - { - "end": 2.08, - "start": 1.68, - "text": "makes" - }, - { - "end": 2.24, - "start": 2.08, - "text": "me" - }, - { - "end": 2.8, - "start": 2.24, - "text": "realize" - }, - { - "end": 3.12, - "start": 2.8, - "text": "just" - }, - { - "end": 3.28, - "start": 3.12, - "text": "how" - }, - { - "end": 3.6, - "start": 3.28, - "text": "much" - }, - { - "end": 3.76, - "start": 3.6, - "text": "Ive" - }, - { - "end": 4.24, - "start": 3.84, - "text": "missed" - }, - { - "end": 4.56, - "start": 4.24, - "text": "you" - }, - { - "end": 4.8, - "start": 4.64, - "text": "We" - }, - { - "end": 5.04, - "start": 4.8, - "text": "have" - }, - { - "end": 5.36, - "start": 5.04, - "text": "so" - }, - { - "end": 5.76, - "start": 5.36, - "text": "much" - }, - { - "end": 5.76, - "start": 5.76, - "text": "to" - }, - { - "end": 6.16, - "start": 5.76, - "text": "catch" - }, - { - "end": 6.4, - "start": 6.16, - "text": "up" - }, - { - "end": 6.72, - "start": 6.4, - "text": "on" - }, - { - "end": 6.96, - "start": 6.8, - "text": "and" - }, - { - "end": 7.04, - "start": 6.96, - "text": "I" - }, - { - "end": 7.36, - "start": 7.04, - "text": "dont" - }, - { - "end": 7.6, - "start": 7.36, - "text": "even" - }, - { - "end": 7.84, - "start": 7.6, - "text": "know" - }, - { - "end": 8.08, - "start": 7.84, - "text": "which" - }, - { - "end": 8.4, - "start": 8.08, - "text": "part" - }, - { - "end": 8.48, - "start": 8.4, - "text": "of" - }, - { - "end": 8.72, - "start": 8.56, - "text": "my" - }, - { - "end": 8.96, - "start": 8.72, - "text": "life" - }, - { - "end": 9.12, - "start": 9.12, - "text": "to" - }, - { - "end": 9.44, - "start": 9.12, - "text": "tell" - }, - { - "end": 9.6, - "start": 9.44, - "text": "you" - }, - { - "end": 10, - "start": 9.6, - "text": "about" - }, - { - "end": 10.48, - "start": 10.08, - "text": "first" - } - ] + "audio_base64": "//uSxImAl...", + "chunk_audio_offset_sec": 16.24, + "chunk_seq": 1, + "content": "Seeing you again now makes me realize just how much I’ve missed you. We have so much to catch up on, and I don’t even know which part of my life to tell you about first." + } + ], + "properties": { + "audio_base64": { + "description": "Base64 encoded audio chunk. Concatenate every chunk in event order to reconstruct the full audio.", + "title": "Audio Base64", + "type": "string" + }, + "content": { + "description": "Text content described by this event's latest alignment snapshot. Long input may be split into multiple content chunks in one stream.", + "title": "Content", + "type": "string" + }, + "alignment": { + "anyOf": [ + { + "$ref": "#/components/schemas/TTSTimestampAlignment" + }, + { + "type": "null" + } + ], + "description": "Latest cumulative timestamp snapshot for `chunk_seq`. When present, replace the previous alignment for the same `chunk_seq`; do not append segments. Null means no alignment snapshot has been produced yet or alignment is unavailable." + }, + "chunk_seq": { + "description": "Sequence number of the text chunk described by `alignment`. Clients should bucket alignment snapshots by this value.", + "minimum": 0, + "title": "Chunk Seq", + "type": "integer" + }, + "chunk_audio_offset_sec": { + "description": "Absolute start time of this text chunk within the full audio, in seconds.", + "minimum": 0, + "title": "Chunk Audio Offset Sec", + "type": "number" + } + }, + "required": [ + "audio_base64", + "content", + "alignment", + "chunk_seq", + "chunk_audio_offset_sec" + ], + "title": "TTSTimestampStreamEvent", + "type": "object" + }, + "examples": { + "first_event": { + "summary": "First text chunk event with alignment", + "value": "data: {\"audio_base64\": \"SUQzBAAAAAAA...\", \"content\": \"I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start.\", \"alignment\": {\"segments\": [{\"text\": \"I\", \"start\": 0.0, \"end\": 0.16}, {\"text\": \"can't\", \"start\": 0.16, \"end\": 0.48}, {\"text\": \"believe\", \"start\": 0.48, \"end\": 0.8}, {\"text\": \"its\", \"start\": 0.8, \"end\": 1.12}, {\"text\": \"been\", \"start\": 1.2, \"end\": 1.44}, {\"text\": \"this\", \"start\": 1.44, \"end\": 1.76}, {\"text\": \"long\", \"start\": 1.76, \"end\": 2.48}, {\"text\": \"It\", \"start\": 2.56, \"end\": 2.64}, {\"text\": \"feels\", \"start\": 2.72, \"end\": 3.04}, {\"text\": \"like\", \"start\": 3.12, \"end\": 3.28}, {\"text\": \"forever\", \"start\": 3.36, \"end\": 4.0}, {\"text\": \"since\", \"start\": 4.0, \"end\": 4.32}, {\"text\": \"we\", \"start\": 4.32, \"end\": 4.48}, {\"text\": \"last\", \"start\": 4.48, \"end\": 4.96}, {\"text\": \"really\", \"start\": 4.96, \"end\": 5.28}, {\"text\": \"talked\", \"start\": 5.28, \"end\": 5.84}, {\"text\": \"Ive\", \"start\": 6.0, \"end\": 6.24}, {\"text\": \"missed\", \"start\": 6.24, \"end\": 6.64}, {\"text\": \"hearing\", \"start\": 6.64, \"end\": 6.96}, {\"text\": \"your\", \"start\": 6.96, \"end\": 7.2}, {\"text\": \"voice\", \"start\": 7.2, \"end\": 7.76}, {\"text\": \"your\", \"start\": 7.76, \"end\": 7.92}, {\"text\": \"stories\", \"start\": 7.92, \"end\": 8.48}, {\"text\": \"even\", \"start\": 8.48, \"end\": 8.72}, {\"text\": \"the\", \"start\": 8.72, \"end\": 8.8}, {\"text\": \"little\", \"start\": 8.8, \"end\": 9.2}, {\"text\": \"things\", \"start\": 9.2, \"end\": 9.52}, {\"text\": \"you\", \"start\": 9.52, \"end\": 9.68}, {\"text\": \"used\", \"start\": 9.68, \"end\": 10.0}, {\"text\": \"to\", \"start\": 10.0, \"end\": 10.08}, {\"text\": \"say\", \"start\": 10.08, \"end\": 10.64}, {\"text\": \"How\", \"start\": 10.64, \"end\": 10.96}, {\"text\": \"have\", \"start\": 10.96, \"end\": 11.12}, {\"text\": \"you\", \"start\": 11.12, \"end\": 11.36}, {\"text\": \"been\", \"start\": 11.36, \"end\": 11.92}, {\"text\": \"Ive\", \"start\": 12.0, \"end\": 12.24}, {\"text\": \"thought\", \"start\": 12.24, \"end\": 12.48}, {\"text\": \"about\", \"start\": 12.48, \"end\": 12.8}, {\"text\": \"calling\", \"start\": 12.8, \"end\": 13.2}, {\"text\": \"you\", \"start\": 13.2, \"end\": 13.36}, {\"text\": \"so\", \"start\": 13.36, \"end\": 13.68}, {\"text\": \"many\", \"start\": 13.68, \"end\": 13.92}, {\"text\": \"times\", \"start\": 13.92, \"end\": 14.56}, {\"text\": \"but\", \"start\": 14.56, \"end\": 14.72}, {\"text\": \"I\", \"start\": 14.72, \"end\": 14.88}, {\"text\": \"never\", \"start\": 14.88, \"end\": 15.2}, {\"text\": \"knew\", \"start\": 15.2, \"end\": 15.36}, {\"text\": \"where\", \"start\": 15.36, \"end\": 15.6}, {\"text\": \"to\", \"start\": 15.6, \"end\": 15.6}, {\"text\": \"start\", \"start\": 15.68, \"end\": 16.24}], \"audio_duration\": 16.24}, \"chunk_seq\": 0, \"chunk_audio_offset_sec\": 0.0}\n\n" + }, + "following_event": { + "summary": "Following audio event with latest alignment snapshot", + "value": "data: {\"audio_base64\": \"//uSxOAAF...\", \"content\": \"I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start.\", \"alignment\": {\"segments\": [{\"text\": \"I\", \"start\": 0.0, \"end\": 0.16}, {\"text\": \"can't\", \"start\": 0.16, \"end\": 0.48}, {\"text\": \"believe\", \"start\": 0.48, \"end\": 0.8}, {\"text\": \"its\", \"start\": 0.8, \"end\": 1.12}, {\"text\": \"been\", \"start\": 1.2, \"end\": 1.44}, {\"text\": \"this\", \"start\": 1.44, \"end\": 1.76}, {\"text\": \"long\", \"start\": 1.76, \"end\": 2.48}, {\"text\": \"It\", \"start\": 2.56, \"end\": 2.64}, {\"text\": \"feels\", \"start\": 2.72, \"end\": 3.04}, {\"text\": \"like\", \"start\": 3.12, \"end\": 3.28}, {\"text\": \"forever\", \"start\": 3.36, \"end\": 4.0}, {\"text\": \"since\", \"start\": 4.0, \"end\": 4.32}, {\"text\": \"we\", \"start\": 4.32, \"end\": 4.48}, {\"text\": \"last\", \"start\": 4.48, \"end\": 4.96}, {\"text\": \"really\", \"start\": 4.96, \"end\": 5.28}, {\"text\": \"talked\", \"start\": 5.28, \"end\": 5.84}, {\"text\": \"Ive\", \"start\": 6.0, \"end\": 6.24}, {\"text\": \"missed\", \"start\": 6.24, \"end\": 6.64}, {\"text\": \"hearing\", \"start\": 6.64, \"end\": 6.96}, {\"text\": \"your\", \"start\": 6.96, \"end\": 7.2}, {\"text\": \"voice\", \"start\": 7.2, \"end\": 7.76}, {\"text\": \"your\", \"start\": 7.76, \"end\": 7.92}, {\"text\": \"stories\", \"start\": 7.92, \"end\": 8.48}, {\"text\": \"even\", \"start\": 8.48, \"end\": 8.72}, {\"text\": \"the\", \"start\": 8.72, \"end\": 8.8}, {\"text\": \"little\", \"start\": 8.8, \"end\": 9.2}, {\"text\": \"things\", \"start\": 9.2, \"end\": 9.52}, {\"text\": \"you\", \"start\": 9.52, \"end\": 9.68}, {\"text\": \"used\", \"start\": 9.68, \"end\": 10.0}, {\"text\": \"to\", \"start\": 10.0, \"end\": 10.08}, {\"text\": \"say\", \"start\": 10.08, \"end\": 10.64}, {\"text\": \"How\", \"start\": 10.64, \"end\": 10.96}, {\"text\": \"have\", \"start\": 10.96, \"end\": 11.12}, {\"text\": \"you\", \"start\": 11.12, \"end\": 11.36}, {\"text\": \"been\", \"start\": 11.36, \"end\": 11.92}, {\"text\": \"Ive\", \"start\": 12.0, \"end\": 12.24}, {\"text\": \"thought\", \"start\": 12.24, \"end\": 12.48}, {\"text\": \"about\", \"start\": 12.48, \"end\": 12.8}, {\"text\": \"calling\", \"start\": 12.8, \"end\": 13.2}, {\"text\": \"you\", \"start\": 13.2, \"end\": 13.36}, {\"text\": \"so\", \"start\": 13.36, \"end\": 13.68}, {\"text\": \"many\", \"start\": 13.68, \"end\": 13.92}, {\"text\": \"times\", \"start\": 13.92, \"end\": 14.56}, {\"text\": \"but\", \"start\": 14.56, \"end\": 14.72}, {\"text\": \"I\", \"start\": 14.72, \"end\": 14.88}, {\"text\": \"never\", \"start\": 14.88, \"end\": 15.2}, {\"text\": \"knew\", \"start\": 15.2, \"end\": 15.36}, {\"text\": \"where\", \"start\": 15.36, \"end\": 15.6}, {\"text\": \"to\", \"start\": 15.6, \"end\": 15.6}, {\"text\": \"start\", \"start\": 15.68, \"end\": 16.24}], \"audio_duration\": 16.24}, \"chunk_seq\": 0, \"chunk_audio_offset_sec\": 0.0}\n\n" + }, + "later_text_chunk_event": { + "summary": "Later text chunk event with another alignment", + "value": "data: {\"audio_base64\": \"//uSxImAl...\", \"content\": \"Seeing you again now makes me realize just how much I’ve missed you. We have so much to catch up on, and I don’t even know which part of my life to tell you about first.\", \"alignment\": {\"segments\": [{\"text\": \"Seeing\", \"start\": 0.4, \"end\": 0.8}, {\"text\": \"you\", \"start\": 0.8, \"end\": 0.96}, {\"text\": \"again\", \"start\": 0.96, \"end\": 1.44}, {\"text\": \"now\", \"start\": 1.44, \"end\": 1.68}, {\"text\": \"makes\", \"start\": 1.68, \"end\": 2.08}, {\"text\": \"me\", \"start\": 2.08, \"end\": 2.24}, {\"text\": \"realize\", \"start\": 2.24, \"end\": 2.8}, {\"text\": \"just\", \"start\": 2.8, \"end\": 3.12}, {\"text\": \"how\", \"start\": 3.12, \"end\": 3.28}, {\"text\": \"much\", \"start\": 3.28, \"end\": 3.6}, {\"text\": \"Ive\", \"start\": 3.6, \"end\": 3.76}, {\"text\": \"missed\", \"start\": 3.84, \"end\": 4.24}, {\"text\": \"you\", \"start\": 4.24, \"end\": 4.56}, {\"text\": \"We\", \"start\": 4.64, \"end\": 4.8}, {\"text\": \"have\", \"start\": 4.8, \"end\": 5.04}, {\"text\": \"so\", \"start\": 5.04, \"end\": 5.36}, {\"text\": \"much\", \"start\": 5.36, \"end\": 5.76}, {\"text\": \"to\", \"start\": 5.76, \"end\": 5.76}, {\"text\": \"catch\", \"start\": 5.76, \"end\": 6.16}, {\"text\": \"up\", \"start\": 6.16, \"end\": 6.4}, {\"text\": \"on\", \"start\": 6.4, \"end\": 6.72}, {\"text\": \"and\", \"start\": 6.8, \"end\": 6.96}, {\"text\": \"I\", \"start\": 6.96, \"end\": 7.04}, {\"text\": \"dont\", \"start\": 7.04, \"end\": 7.36}, {\"text\": \"even\", \"start\": 7.36, \"end\": 7.6}, {\"text\": \"know\", \"start\": 7.6, \"end\": 7.84}, {\"text\": \"which\", \"start\": 7.84, \"end\": 8.08}, {\"text\": \"part\", \"start\": 8.08, \"end\": 8.4}, {\"text\": \"of\", \"start\": 8.4, \"end\": 8.48}, {\"text\": \"my\", \"start\": 8.56, \"end\": 8.72}, {\"text\": \"life\", \"start\": 8.72, \"end\": 8.96}, {\"text\": \"to\", \"start\": 9.12, \"end\": 9.12}, {\"text\": \"tell\", \"start\": 9.12, \"end\": 9.44}, {\"text\": \"you\", \"start\": 9.44, \"end\": 9.6}, {\"text\": \"about\", \"start\": 9.6, \"end\": 10.0}, {\"text\": \"first\", \"start\": 10.08, \"end\": 10.48}], \"audio_duration\": 10.48}, \"chunk_seq\": 1, \"chunk_audio_offset_sec\": 16.24}\n\n" + } + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "402": { + "description": "No payment -- see charging schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "OpenAPI v1" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Stream With Timestamps", + "source": "curl --no-buffer --request POST \\\n --url https://api.fish.audio/v1/tts/stream/with-timestamp \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: s2.1-pro-free' \\\n --data '{\n \"text\": \"[happy] I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start. Seeing you again now makes me realize just how much I’ve missed you. We have so much to catch up on, and I don’t even know which part of my life to tell you about first.\",\n \"format\": \"opus\",\n \"normalize\": true,\n \"temperature\": 0.9,\n \"chunk_length\": 100,\n \"top_p\": 0.9,\n \"latency\": \"balanced\",\n \"sample_rate\": 48000,\n \"reference_id\": \"fbe02f8306fc4d3d915e9871722a39d5\"\n }'" + } + ] + } + }, + "/v1/tts/live/with-timestamp": { + "get": { + "summary": "Text to Speech Live with Timestamps (WebSocket)", + "description": "Streaming text-to-speech over a WebSocket that returns word-level timestamps\nalongside the audio. Connect to `wss://api.fish.audio/v1/tts/live/with-timestamp`;\nthis is a WebSocket upgrade, not a plain HTTP request.\n\nUse it when you need to feed text in as it is produced — an LLM token stream,\na live caption feed — and want word timings for lip-sync, subtitles or\nhighlighting. For a one-shot request use `POST /v1/tts/stream/with-timestamp`\ninstead.\n\n## Handshake\n\nSend the same headers as any other API call:\n\n- `Authorization: Bearer `\n- `model: ` — optional, defaults to `s2.1-pro`.\n\n## Framing\n\nEvery frame in both directions is a MessagePack-encoded **binary** frame\nholding a map with an `event` key. Text frames are ignored by the server and\nshould be ignored by your client.\n\n## Client to server\n\nThe inbound protocol is identical to `/v1/tts/live`:\n\n- `start` — opens the session. Send it once per session, before any other\n event. Carries a `request` object with the same fields as the\n `POST /v1/tts` body; leave `text` empty and stream the words with `text`\n events. After a `finish` you may send another `start` to run a new session\n on the same socket.\n- `text` — appends a text fragment to the session buffer.\n- `flush` — forces buffered text through generation without waiting for the\n chunk to fill. The session stays open.\n- `stop` — ends the session. The server drains the buffer and answers with a\n final `finish` event.\n\n## Server to client\n\n- `audio` — one audio chunk. Beyond the `audio` bytes it carries `content`,\n `alignment`, `chunk_seq` and `chunk_audio_offset_sec`; these last three are\n what this endpoint adds over `/v1/tts/live`. Concatenate every `audio`\n payload in arrival order to reconstruct the full audio. Note that `audio`\n here is raw MessagePack `bin`, not the base64 string used by the SSE\n endpoint.\n- `finish` — terminal event, with `reason` of `stop` or `error`.\n- `error` — a request-level failure such as an invalid `start` request or a\n concurrency refusal; the socket closes afterwards.\n\n## Reading the alignment\n\n`alignment` is a **cumulative snapshot** for its `chunk_seq`, not a delta:\nwhen it is non-null, replace the alignment you stored for that chunk rather\nthan appending to it. A `null` alignment means nothing new has been committed\nfor the chunk yet, which is normal early on.\n\nSegment times inside `alignment` are relative to the start of their chunk.\n`chunk_audio_offset_sec` is the chunk's own start time within the full audio,\nso the absolute time of a word is `chunk_audio_offset_sec + segment.start`.\nBoth `chunk_seq` and `chunk_audio_offset_sec` are monotonically\nnon-decreasing. A frame may be labelled with the previous chunk's\n`chunk_seq` while carrying the next chunk's audio bytes, so never use\n`chunk_seq` to attribute audio bytes — only alignment.", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "header", + "name": "model", + "description": "Specify which TTS model to use. Use `s2.1-pro-free` for the free developer tier. `drama-3-preview` is a preview model; its behavior and availability may change. If omitted or set to an unrecognized value, the request falls back to `s2.1-pro`.", + "required": false, + "schema": { + "default": "s2.1-pro", + "enum": [ + "s1", + "s2-pro", + "s2.1-pro", + "s2.1-pro-free", + "drama-3-preview" + ], + "title": "Model", + "type": "string" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "WebSocket handshake accepted (the server answers `101 Switching Protocols` on the wire). Every frame the server then sends is a MessagePack-encoded binary frame holding one of the events below. `audio` events carry the audio chunk together with `alignment`, `chunk_seq` and `chunk_audio_offset_sec`; `alignment` is the cumulative snapshot for its `chunk_seq` and replaces the previous one rather than extending it. Segment times are relative to the chunk, so absolute time is `chunk_audio_offset_sec + segment.start`. The stream ends with a `finish` event.", + "content": { + "application/msgpack": { + "schema": { + "discriminator": { + "mapping": { + "audio": "#/components/schemas/TTSLiveWithTimestampAudioEvent", + "error": "#/components/schemas/TTSLiveErrorEvent", + "finish": "#/components/schemas/TTSLiveFinishEvent" + }, + "propertyName": "event" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TTSLiveWithTimestampAudioEvent" + }, + { + "$ref": "#/components/schemas/TTSLiveFinishEvent" + }, + { + "$ref": "#/components/schemas/TTSLiveErrorEvent" + } + ] + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "402": { + "description": "No payment -- see charging schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "OpenAPI v1" + ], + "x-codeSamples": [ + { + "lang": "python", + "label": "Live WebSocket With Timestamps", + "source": "import asyncio\n\nimport ormsgpack\nimport websockets\n\nTEXT = \"Hello there. This is a live synthesis, streamed word by word.\"\n\n\nasync def send_text(socket) -> None:\n # `request` takes the same fields as POST /v1/tts. Leave `text` empty and\n # stream the words in as they become available.\n await socket.send(\n ormsgpack.packb(\n {\n \"event\": \"start\",\n \"request\": {\n \"text\": \"\",\n \"format\": \"opus\",\n \"latency\": \"balanced\",\n \"chunk_length\": 100,\n \"reference_id\": \"fbe02f8306fc4d3d915e9871722a39d5\",\n },\n }\n )\n )\n for word in TEXT.split(\" \"):\n await socket.send(ormsgpack.packb({\"event\": \"text\", \"text\": word + \" \"}))\n await socket.send(ormsgpack.packb({\"event\": \"stop\"}))\n\n\nasync def main() -> None:\n async with websockets.connect(\n \"wss://api.fish.audio/v1/tts/live/with-timestamp\",\n additional_headers={\n \"Authorization\": \"Bearer \",\n \"model\": \"s2.1-pro-free\",\n },\n max_size=None,\n ) as socket:\n # Send and receive concurrently. Sending everything before reading can\n # stall on flow control once the audio outpaces your receive buffer.\n sender = asyncio.create_task(send_text(socket))\n\n audio = bytearray()\n alignments: dict[int, dict] = {}\n offsets: dict[int, float] = {}\n\n try:\n async for frame in socket:\n if isinstance(frame, str):\n continue\n message = ormsgpack.unpackb(frame)\n if message[\"event\"] == \"audio\":\n audio += message[\"audio\"]\n offsets[message[\"chunk_seq\"]] = message[\"chunk_audio_offset_sec\"]\n if message[\"alignment\"] is not None:\n # Cumulative snapshot: replace the chunk, never append.\n alignments[message[\"chunk_seq\"]] = message[\"alignment\"]\n elif message[\"event\"] == \"finish\":\n if message[\"reason\"] == \"error\":\n raise RuntimeError(message.get(\"message\", \"synthesis failed\"))\n break\n elif message[\"event\"] == \"error\":\n raise RuntimeError(message[\"error\"])\n finally:\n sender.cancel()\n\n with open(\"output.opus\", \"wb\") as f:\n f.write(audio)\n\n # Segment times are chunk-relative; fold in the offset to get absolute.\n for chunk_seq in sorted(alignments):\n offset = offsets[chunk_seq]\n for segment in alignments[chunk_seq][\"segments\"]:\n start = offset + segment[\"start\"]\n end = offset + segment[\"end\"]\n print(f\"{start:6.2f}s -> {end:6.2f}s {segment['text']}\")\n\n\nasyncio.run(main())" + } + ], + "x-fish-websocket": { + "url": "wss://api.fish.audio/v1/tts/live/with-timestamp", + "encoding": "application/msgpack", + "clientEvents": { + "description": "Frames the client sends, MessagePack encoded. Identical to the `/v1/tts/live` inbound protocol: `start` once, then any number of `text` events, optional `flush` events, and a final `stop`.", + "schema": { + "discriminator": { + "mapping": { + "flush": "#/components/schemas/TTSLiveFlushEvent", + "start": "#/components/schemas/TTSLiveStartEvent", + "stop": "#/components/schemas/TTSLiveStopEvent", + "text": "#/components/schemas/TTSLiveTextEvent" + }, + "propertyName": "event" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TTSLiveStartEvent" + }, + { + "$ref": "#/components/schemas/TTSLiveTextEvent" + }, + { + "$ref": "#/components/schemas/TTSLiveFlushEvent" + }, + { + "$ref": "#/components/schemas/TTSLiveStopEvent" + } + ] + } + } + } + } + }, + "/v1/voice-design": { + "post": { + "summary": "Voice Design", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "header", + "name": "model", + "description": "Specify which voice-design model to use.", + "required": true, + "schema": { + "const": "voice-design-1", + "default": "voice-design-1", + "title": "Model", + "type": "string" + }, + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoiceDesignRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "candidates": { + "description": "Generated voice candidates.", + "items": { + "$ref": "#/components/schemas/VoiceDesignCandidate" + }, + "title": "Candidates", + "type": "array" + } + }, + "required": [ + "candidates" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "402": { + "description": "No payment -- see charging schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "OpenAPI v1" + ], + "x-codeSamples": [ + { + "lang": "bash", + "label": "Voice Design", + "source": "curl --request POST \\\n --url https://api.fish.audio/v1/voice-design \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: voice-design-1' \\\n --data '{\n \"instruction\": \"Warm, confident studio narrator with a natural tone\",\n \"reference_text\": \"Welcome to Fish Audio.\",\n \"language\": \"en\",\n \"n\": 2,\n \"speed\": 1,\n \"num_step\": 32,\n \"guidance_scale\": 2,\n \"instruct_guidance_scale\": 0,\n \"seed\": 42\n }'" + } + ] + } + }, + "/model": { + "get": { + "summary": "List Models", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "page_size", + "description": "Page size", + "required": false, + "schema": { + "default": 10, + "maximum": 100, + "minimum": 1, + "title": "Page Size", + "type": "integer" + }, + "deprecated": false + }, + { + "in": "query", + "name": "page_number", + "description": "Page number", + "required": false, + "schema": { + "default": 1, + "minimum": 1, + "title": "Page Number", + "type": "integer" + }, + "deprecated": false + }, + { + "in": "query", + "name": "title", + "description": "Title to filter models", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Title" + }, + "deprecated": false + }, + { + "in": "query", + "name": "tag", + "description": "Tag to filter models", + "required": false, + "schema": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tag" + }, + "deprecated": false + }, + { + "in": "query", + "name": "self", + "description": "If True, return models owned by the active workspace", + "required": false, + "schema": { + "default": false, + "title": "Self", + "type": "boolean" + }, + "deprecated": false + }, + { + "in": "query", + "name": "author_id", + "description": "Author ID to filter public models; ignored if self is True", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Author Id" + }, + "deprecated": false + }, + { + "in": "query", + "name": "language", + "description": "Language to filter models", + "required": false, + "schema": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Language" + }, + "deprecated": false + }, + { + "in": "query", + "name": "title_language", + "description": "Title language to filter models", + "required": false, + "schema": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Title Language" + }, + "deprecated": false + }, + { + "in": "query", + "name": "licensed", + "description": "If True, only voices licensed by Fish Audio (rights secured from the voice owner) are returned; ignored if self is True", + "required": false, + "schema": { + "default": false, + "title": "Licensed", + "type": "boolean" + }, + "deprecated": false + }, + { + "in": "query", + "name": "sort_by", + "description": "", + "required": false, + "schema": { + "default": "score", + "enum": [ + "score", + "task_count", + "created_at" + ], + "title": "Sort By", + "type": "string" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "max_offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Max Offset" + }, + "accessible_upper_bound": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Accessible Upper Bound" + }, + "window_limited": { + "default": false, + "title": "Window Limited", + "type": "boolean" + }, + "total_is_exact": { + "default": true, + "title": "Total Is Exact", + "type": "boolean" + }, + "total": { + "title": "Total", + "type": "integer" + }, + "items": { + "items": { + "$ref": "#/components/schemas/ModelEntity" + }, + "title": "Items", + "type": "array" + }, + "has_more": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Has More" + } + }, + "required": [ + "total", + "items" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Model" + ] + }, + "post": { + "summary": "Create Model for Users via API", + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "visibility": { + "default": "private", + "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", + "enum": [ + "public", + "unlist", + "private" + ], + "title": "Visibility", + "type": "string" + }, + "type": { + "const": "tts", + "description": "Model type, tts is for text to speech", + "title": "Type", + "type": "string" + }, + "title": { + "description": "Model title or name", + "title": "Title", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model description", + "title": "Description" + }, + "cover_image": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model cover image, this is required if the model is public", + "title": "Cover Image" + }, + "train_mode": { + "const": "fast", + "description": "Model train mode, for TTS model, fast means model instantly available after creation", + "title": "Train Mode", + "type": "string" + }, + "voices": { + "anyOf": [ + { + "items": { + "format": "binary", + "type": "string" + }, + "maxItems": 20, + "minItems": 1, + "type": "array" + }, + { + "format": "binary", + "type": "string" + } + ], + "description": "Upload voices files that will be used to tune the model", + "title": "Voices" + }, + "texts": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", + "title": "Texts" + }, + "voice_design_signatures": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", + "title": "Voice Design Signatures" + }, + "tags": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Model tags", + "title": "Tags" + }, + "enhance_audio_quality": { + "default": true, + "description": "Enhance audio quality", + "title": "Enhance Audio Quality", + "type": "boolean" + }, + "generate_sample": { + "default": false, + "description": "Generate default text", + "title": "Generate Sample", + "type": "boolean" + } + }, + "required": [ + "type", + "title", + "train_mode", + "voices" + ], + "type": "object" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "visibility": { + "default": "private", + "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", + "enum": [ + "public", + "unlist", + "private" + ], + "title": "Visibility", + "type": "string" + }, + "type": { + "const": "tts", + "description": "Model type, tts is for text to speech", + "title": "Type", + "type": "string" + }, + "title": { + "description": "Model title or name", + "title": "Title", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model description", + "title": "Description" + }, + "cover_image": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model cover image, this is required if the model is public", + "title": "Cover Image" + }, + "train_mode": { + "const": "fast", + "description": "Model train mode, for TTS model, fast means model instantly available after creation", + "title": "Train Mode", + "type": "string" + }, + "voices": { + "anyOf": [ + { + "items": { + "format": "binary", + "type": "string" + }, + "maxItems": 20, + "minItems": 1, + "type": "array" + }, + { + "format": "binary", + "type": "string" + } + ], + "description": "Upload voices files that will be used to tune the model", + "title": "Voices" + }, + "texts": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", + "title": "Texts" + }, + "voice_design_signatures": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", + "title": "Voice Design Signatures" + }, + "tags": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Model tags", + "title": "Tags" + }, + "enhance_audio_quality": { + "default": true, + "description": "Enhance audio quality", + "title": "Enhance Audio Quality", + "type": "boolean" + }, + "generate_sample": { + "default": false, + "description": "Generate default text", + "title": "Generate Sample", + "type": "boolean" + } + }, + "required": [ + "type", + "title", + "train_mode", + "voices" + ], + "type": "object" + } + }, + "multipart/form-data": { + "schema": { + "properties": { + "visibility": { + "default": "private", + "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", + "enum": [ + "public", + "unlist", + "private" + ], + "title": "Visibility", + "type": "string" + }, + "type": { + "const": "tts", + "description": "Model type, tts is for text to speech", + "title": "Type", + "type": "string" + }, + "title": { + "description": "Model title or name", + "title": "Title", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model description", + "title": "Description" + }, + "cover_image": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model cover image, this is required if the model is public", + "title": "Cover Image" + }, + "train_mode": { + "const": "fast", + "description": "Model train mode, for TTS model, fast means model instantly available after creation", + "title": "Train Mode", + "type": "string" + }, + "voices": { + "anyOf": [ + { + "items": { + "format": "binary", + "type": "string" + }, + "maxItems": 20, + "minItems": 1, + "type": "array" + }, + { + "format": "binary", + "type": "string" + } + ], + "description": "Upload voices files that will be used to tune the model", + "title": "Voices" + }, + "texts": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + { + "type": "string" }, - "audio_base64": "//uSxImAl...", - "chunk_audio_offset_sec": 16.24, - "chunk_seq": 1, - "content": "Seeing you again now makes me realize just how much I’ve missed you. We have so much to catch up on, and I don’t even know which part of my life to tell you about first." - } - ], - "properties": { - "audio_base64": { - "description": "Base64 encoded audio chunk. Concatenate every chunk in event order to reconstruct the full audio.", - "title": "Audio Base64", - "type": "string" - }, - "content": { - "description": "Text content described by this event's latest alignment snapshot. Long input may be split into multiple content chunks in one stream.", - "title": "Content", - "type": "string" - }, - "alignment": { - "anyOf": [ - { - "$ref": "#/components/schemas/TTSTimestampAlignment" + { + "type": "null" + } + ], + "default": null, + "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", + "title": "Texts" + }, + "voice_design_signatures": { + "anyOf": [ + { + "items": { + "type": "string" }, - { - "type": "null" - } - ], - "description": "Latest cumulative timestamp snapshot for `chunk_seq`. When present, replace the previous alignment for the same `chunk_seq`; do not append segments. Null means no alignment snapshot has been produced yet or alignment is unavailable." - }, - "chunk_seq": { - "description": "Sequence number of the text chunk described by `alignment`. Clients should bucket alignment snapshots by this value.", - "minimum": 0, - "title": "Chunk Seq", - "type": "integer" - }, - "chunk_audio_offset_sec": { - "description": "Absolute start time of this text chunk within the full audio, in seconds.", - "minimum": 0, - "title": "Chunk Audio Offset Sec", - "type": "number" - } + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", + "title": "Voice Design Signatures" }, - "required": [ - "audio_base64", - "content", - "alignment", - "chunk_seq", - "chunk_audio_offset_sec" - ], - "title": "TTSTimestampStreamEvent", - "type": "object" - }, - "examples": { - "first_event": { - "summary": "First text chunk event with alignment", - "value": "data: {\"audio_base64\": \"SUQzBAAAAAAA...\", \"content\": \"I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start.\", \"alignment\": {\"segments\": [{\"text\": \"I\", \"start\": 0.0, \"end\": 0.16}, {\"text\": \"can't\", \"start\": 0.16, \"end\": 0.48}, {\"text\": \"believe\", \"start\": 0.48, \"end\": 0.8}, {\"text\": \"its\", \"start\": 0.8, \"end\": 1.12}, {\"text\": \"been\", \"start\": 1.2, \"end\": 1.44}, {\"text\": \"this\", \"start\": 1.44, \"end\": 1.76}, {\"text\": \"long\", \"start\": 1.76, \"end\": 2.48}, {\"text\": \"It\", \"start\": 2.56, \"end\": 2.64}, {\"text\": \"feels\", \"start\": 2.72, \"end\": 3.04}, {\"text\": \"like\", \"start\": 3.12, \"end\": 3.28}, {\"text\": \"forever\", \"start\": 3.36, \"end\": 4.0}, {\"text\": \"since\", \"start\": 4.0, \"end\": 4.32}, {\"text\": \"we\", \"start\": 4.32, \"end\": 4.48}, {\"text\": \"last\", \"start\": 4.48, \"end\": 4.96}, {\"text\": \"really\", \"start\": 4.96, \"end\": 5.28}, {\"text\": \"talked\", \"start\": 5.28, \"end\": 5.84}, {\"text\": \"Ive\", \"start\": 6.0, \"end\": 6.24}, {\"text\": \"missed\", \"start\": 6.24, \"end\": 6.64}, {\"text\": \"hearing\", \"start\": 6.64, \"end\": 6.96}, {\"text\": \"your\", \"start\": 6.96, \"end\": 7.2}, {\"text\": \"voice\", \"start\": 7.2, \"end\": 7.76}, {\"text\": \"your\", \"start\": 7.76, \"end\": 7.92}, {\"text\": \"stories\", \"start\": 7.92, \"end\": 8.48}, {\"text\": \"even\", \"start\": 8.48, \"end\": 8.72}, {\"text\": \"the\", \"start\": 8.72, \"end\": 8.8}, {\"text\": \"little\", \"start\": 8.8, \"end\": 9.2}, {\"text\": \"things\", \"start\": 9.2, \"end\": 9.52}, {\"text\": \"you\", \"start\": 9.52, \"end\": 9.68}, {\"text\": \"used\", \"start\": 9.68, \"end\": 10.0}, {\"text\": \"to\", \"start\": 10.0, \"end\": 10.08}, {\"text\": \"say\", \"start\": 10.08, \"end\": 10.64}, {\"text\": \"How\", \"start\": 10.64, \"end\": 10.96}, {\"text\": \"have\", \"start\": 10.96, \"end\": 11.12}, {\"text\": \"you\", \"start\": 11.12, \"end\": 11.36}, {\"text\": \"been\", \"start\": 11.36, \"end\": 11.92}, {\"text\": \"Ive\", \"start\": 12.0, \"end\": 12.24}, {\"text\": \"thought\", \"start\": 12.24, \"end\": 12.48}, {\"text\": \"about\", \"start\": 12.48, \"end\": 12.8}, {\"text\": \"calling\", \"start\": 12.8, \"end\": 13.2}, {\"text\": \"you\", \"start\": 13.2, \"end\": 13.36}, {\"text\": \"so\", \"start\": 13.36, \"end\": 13.68}, {\"text\": \"many\", \"start\": 13.68, \"end\": 13.92}, {\"text\": \"times\", \"start\": 13.92, \"end\": 14.56}, {\"text\": \"but\", \"start\": 14.56, \"end\": 14.72}, {\"text\": \"I\", \"start\": 14.72, \"end\": 14.88}, {\"text\": \"never\", \"start\": 14.88, \"end\": 15.2}, {\"text\": \"knew\", \"start\": 15.2, \"end\": 15.36}, {\"text\": \"where\", \"start\": 15.36, \"end\": 15.6}, {\"text\": \"to\", \"start\": 15.6, \"end\": 15.6}, {\"text\": \"start\", \"start\": 15.68, \"end\": 16.24}], \"audio_duration\": 16.24}, \"chunk_seq\": 0, \"chunk_audio_offset_sec\": 0.0}\n\n" + "tags": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Model tags", + "title": "Tags" }, - "following_event": { - "summary": "Following audio event with latest alignment snapshot", - "value": "data: {\"audio_base64\": \"//uSxOAAF...\", \"content\": \"I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start.\", \"alignment\": {\"segments\": [{\"text\": \"I\", \"start\": 0.0, \"end\": 0.16}, {\"text\": \"can't\", \"start\": 0.16, \"end\": 0.48}, {\"text\": \"believe\", \"start\": 0.48, \"end\": 0.8}, {\"text\": \"its\", \"start\": 0.8, \"end\": 1.12}, {\"text\": \"been\", \"start\": 1.2, \"end\": 1.44}, {\"text\": \"this\", \"start\": 1.44, \"end\": 1.76}, {\"text\": \"long\", \"start\": 1.76, \"end\": 2.48}, {\"text\": \"It\", \"start\": 2.56, \"end\": 2.64}, {\"text\": \"feels\", \"start\": 2.72, \"end\": 3.04}, {\"text\": \"like\", \"start\": 3.12, \"end\": 3.28}, {\"text\": \"forever\", \"start\": 3.36, \"end\": 4.0}, {\"text\": \"since\", \"start\": 4.0, \"end\": 4.32}, {\"text\": \"we\", \"start\": 4.32, \"end\": 4.48}, {\"text\": \"last\", \"start\": 4.48, \"end\": 4.96}, {\"text\": \"really\", \"start\": 4.96, \"end\": 5.28}, {\"text\": \"talked\", \"start\": 5.28, \"end\": 5.84}, {\"text\": \"Ive\", \"start\": 6.0, \"end\": 6.24}, {\"text\": \"missed\", \"start\": 6.24, \"end\": 6.64}, {\"text\": \"hearing\", \"start\": 6.64, \"end\": 6.96}, {\"text\": \"your\", \"start\": 6.96, \"end\": 7.2}, {\"text\": \"voice\", \"start\": 7.2, \"end\": 7.76}, {\"text\": \"your\", \"start\": 7.76, \"end\": 7.92}, {\"text\": \"stories\", \"start\": 7.92, \"end\": 8.48}, {\"text\": \"even\", \"start\": 8.48, \"end\": 8.72}, {\"text\": \"the\", \"start\": 8.72, \"end\": 8.8}, {\"text\": \"little\", \"start\": 8.8, \"end\": 9.2}, {\"text\": \"things\", \"start\": 9.2, \"end\": 9.52}, {\"text\": \"you\", \"start\": 9.52, \"end\": 9.68}, {\"text\": \"used\", \"start\": 9.68, \"end\": 10.0}, {\"text\": \"to\", \"start\": 10.0, \"end\": 10.08}, {\"text\": \"say\", \"start\": 10.08, \"end\": 10.64}, {\"text\": \"How\", \"start\": 10.64, \"end\": 10.96}, {\"text\": \"have\", \"start\": 10.96, \"end\": 11.12}, {\"text\": \"you\", \"start\": 11.12, \"end\": 11.36}, {\"text\": \"been\", \"start\": 11.36, \"end\": 11.92}, {\"text\": \"Ive\", \"start\": 12.0, \"end\": 12.24}, {\"text\": \"thought\", \"start\": 12.24, \"end\": 12.48}, {\"text\": \"about\", \"start\": 12.48, \"end\": 12.8}, {\"text\": \"calling\", \"start\": 12.8, \"end\": 13.2}, {\"text\": \"you\", \"start\": 13.2, \"end\": 13.36}, {\"text\": \"so\", \"start\": 13.36, \"end\": 13.68}, {\"text\": \"many\", \"start\": 13.68, \"end\": 13.92}, {\"text\": \"times\", \"start\": 13.92, \"end\": 14.56}, {\"text\": \"but\", \"start\": 14.56, \"end\": 14.72}, {\"text\": \"I\", \"start\": 14.72, \"end\": 14.88}, {\"text\": \"never\", \"start\": 14.88, \"end\": 15.2}, {\"text\": \"knew\", \"start\": 15.2, \"end\": 15.36}, {\"text\": \"where\", \"start\": 15.36, \"end\": 15.6}, {\"text\": \"to\", \"start\": 15.6, \"end\": 15.6}, {\"text\": \"start\", \"start\": 15.68, \"end\": 16.24}], \"audio_duration\": 16.24}, \"chunk_seq\": 0, \"chunk_audio_offset_sec\": 0.0}\n\n" + "enhance_audio_quality": { + "default": true, + "description": "Enhance audio quality", + "title": "Enhance Audio Quality", + "type": "boolean" }, - "later_text_chunk_event": { - "summary": "Later text chunk event with another alignment", - "value": "data: {\"audio_base64\": \"//uSxImAl...\", \"content\": \"Seeing you again now makes me realize just how much I’ve missed you. We have so much to catch up on, and I don’t even know which part of my life to tell you about first.\", \"alignment\": {\"segments\": [{\"text\": \"Seeing\", \"start\": 0.4, \"end\": 0.8}, {\"text\": \"you\", \"start\": 0.8, \"end\": 0.96}, {\"text\": \"again\", \"start\": 0.96, \"end\": 1.44}, {\"text\": \"now\", \"start\": 1.44, \"end\": 1.68}, {\"text\": \"makes\", \"start\": 1.68, \"end\": 2.08}, {\"text\": \"me\", \"start\": 2.08, \"end\": 2.24}, {\"text\": \"realize\", \"start\": 2.24, \"end\": 2.8}, {\"text\": \"just\", \"start\": 2.8, \"end\": 3.12}, {\"text\": \"how\", \"start\": 3.12, \"end\": 3.28}, {\"text\": \"much\", \"start\": 3.28, \"end\": 3.6}, {\"text\": \"Ive\", \"start\": 3.6, \"end\": 3.76}, {\"text\": \"missed\", \"start\": 3.84, \"end\": 4.24}, {\"text\": \"you\", \"start\": 4.24, \"end\": 4.56}, {\"text\": \"We\", \"start\": 4.64, \"end\": 4.8}, {\"text\": \"have\", \"start\": 4.8, \"end\": 5.04}, {\"text\": \"so\", \"start\": 5.04, \"end\": 5.36}, {\"text\": \"much\", \"start\": 5.36, \"end\": 5.76}, {\"text\": \"to\", \"start\": 5.76, \"end\": 5.76}, {\"text\": \"catch\", \"start\": 5.76, \"end\": 6.16}, {\"text\": \"up\", \"start\": 6.16, \"end\": 6.4}, {\"text\": \"on\", \"start\": 6.4, \"end\": 6.72}, {\"text\": \"and\", \"start\": 6.8, \"end\": 6.96}, {\"text\": \"I\", \"start\": 6.96, \"end\": 7.04}, {\"text\": \"dont\", \"start\": 7.04, \"end\": 7.36}, {\"text\": \"even\", \"start\": 7.36, \"end\": 7.6}, {\"text\": \"know\", \"start\": 7.6, \"end\": 7.84}, {\"text\": \"which\", \"start\": 7.84, \"end\": 8.08}, {\"text\": \"part\", \"start\": 8.08, \"end\": 8.4}, {\"text\": \"of\", \"start\": 8.4, \"end\": 8.48}, {\"text\": \"my\", \"start\": 8.56, \"end\": 8.72}, {\"text\": \"life\", \"start\": 8.72, \"end\": 8.96}, {\"text\": \"to\", \"start\": 9.12, \"end\": 9.12}, {\"text\": \"tell\", \"start\": 9.12, \"end\": 9.44}, {\"text\": \"you\", \"start\": 9.44, \"end\": 9.6}, {\"text\": \"about\", \"start\": 9.6, \"end\": 10.0}, {\"text\": \"first\", \"start\": 10.08, \"end\": 10.48}], \"audio_duration\": 10.48}, \"chunk_seq\": 1, \"chunk_audio_offset_sec\": 16.24}\n\n" + "generate_sample": { + "default": false, + "description": "Generate default text", + "title": "Generate Sample", + "type": "boolean" } - } + }, + "required": [ + "type", + "title", + "train_mode", + "voices" + ], + "type": "object" } - } - }, - "401": { - "description": "No permission -- see authorization schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { + }, + "application/msgpack": { + "schema": { + "properties": { + "visibility": { + "default": "private", + "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", + "enum": [ + "public", + "unlist", + "private" + ], + "title": "Visibility", + "type": "string" + }, + "type": { + "const": "tts", + "description": "Model type, tts is for text to speech", + "title": "Type", + "type": "string" + }, + "title": { + "description": "Model title or name", + "title": "Title", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model description", + "title": "Description" + }, + "cover_image": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model cover image, this is required if the model is public", + "title": "Cover Image" + }, + "train_mode": { + "const": "fast", + "description": "Model train mode, for TTS model, fast means model instantly available after creation", + "title": "Train Mode", + "type": "string" + }, + "voices": { + "anyOf": [ + { + "items": { + "format": "binary", "type": "string" }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } + "maxItems": 20, + "minItems": 1, + "type": "array" + }, + { + "format": "binary", + "type": "string" + } + ], + "description": "Upload voices files that will be used to tune the model", + "title": "Voices" }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - }, - "402": { - "description": "No payment -- see charging schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { + "texts": { + "anyOf": [ + { + "items": { "type": "string" }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } + "maxItems": 20, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", + "title": "Texts" + }, + "voice_design_signatures": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", + "title": "Voice Design Signatures" + }, + "tags": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Model tags", + "title": "Tags" }, - "required": [ - "status", - "message" - ], - "type": "object" - } + "enhance_audio_quality": { + "default": true, + "description": "Enhance audio quality", + "title": "Enhance Audio Quality", + "type": "boolean" + }, + "generate_sample": { + "default": false, + "description": "Generate default text", + "title": "Generate Sample", + "type": "boolean" + } + }, + "required": [ + "type", + "title", + "train_mode", + "voices" + ], + "type": "object" } } - }, - "503": { - "description": "The server cannot process the request due to a high load", + } + }, + "responses": { + "201": { + "description": "Document created, URL follows", "headers": {}, "content": { "application/json": { "schema": { "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", + "_id": { + "title": "Id", "type": "string" }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } + "type": { + "enum": [ + "svc", + "tts" ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - } - }, - "tags": [ - "OpenAPI v1" - ], - "x-codeSamples": [ - { - "lang": "bash", - "label": "Stream With Timestamps", - "source": "curl --no-buffer --request POST \\\n --url https://api.fish.audio/v1/tts/stream/with-timestamp \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: s2.1-pro-free' \\\n --data '{\n \"text\": \"[happy] I can’t believe it’s been this long. It feels like forever since we last really talked. I’ve missed hearing your voice, your stories, even the little things you used to say. How have you been? I’ve thought about calling you so many times, but I never knew where to start. Seeing you again now makes me realize just how much I’ve missed you. We have so much to catch up on, and I don’t even know which part of my life to tell you about first.\",\n \"format\": \"opus\",\n \"normalize\": true,\n \"temperature\": 0.9,\n \"chunk_length\": 100,\n \"top_p\": 0.9,\n \"latency\": \"balanced\",\n \"sample_rate\": 48000,\n \"reference_id\": \"fbe02f8306fc4d3d915e9871722a39d5\"\n }'" - } - ] - } - }, - "/v1/tts/live/with-timestamp": { - "get": { - "summary": "Text to Speech Live with Timestamps (WebSocket)", - "description": "Streaming text-to-speech over a WebSocket that returns word-level timestamps\nalongside the audio. Connect to `wss://api.fish.audio/v1/tts/live/with-timestamp`;\nthis is a WebSocket upgrade, not a plain HTTP request.\n\nUse it when you need to feed text in as it is produced — an LLM token stream,\na live caption feed — and want word timings for lip-sync, subtitles or\nhighlighting. For a one-shot request use `POST /v1/tts/stream/with-timestamp`\ninstead.\n\n## Handshake\n\nSend the same headers as any other API call:\n\n- `Authorization: Bearer `\n- `model: ` — optional, defaults to `s2.1-pro`.\n\n## Framing\n\nEvery frame in both directions is a MessagePack-encoded **binary** frame\nholding a map with an `event` key. Text frames are ignored by the server and\nshould be ignored by your client.\n\n## Client to server\n\nThe inbound protocol is identical to `/v1/tts/live`:\n\n- `start` — opens the session. Send it once per session, before any other\n event. Carries a `request` object with the same fields as the\n `POST /v1/tts` body; leave `text` empty and stream the words with `text`\n events. After a `finish` you may send another `start` to run a new session\n on the same socket.\n- `text` — appends a text fragment to the session buffer.\n- `flush` — forces buffered text through generation without waiting for the\n chunk to fill. The session stays open.\n- `stop` — ends the session. The server drains the buffer and answers with a\n final `finish` event.\n\n## Server to client\n\n- `audio` — one audio chunk. Beyond the `audio` bytes it carries `content`,\n `alignment`, `chunk_seq` and `chunk_audio_offset_sec`; these last three are\n what this endpoint adds over `/v1/tts/live`. Concatenate every `audio`\n payload in arrival order to reconstruct the full audio. Note that `audio`\n here is raw MessagePack `bin`, not the base64 string used by the SSE\n endpoint.\n- `finish` — terminal event, with `reason` of `stop` or `error`.\n- `error` — a request-level failure such as an invalid `start` request or a\n concurrency refusal; the socket closes afterwards.\n\n## Reading the alignment\n\n`alignment` is a **cumulative snapshot** for its `chunk_seq`, not a delta:\nwhen it is non-null, replace the alignment you stored for that chunk rather\nthan appending to it. A `null` alignment means nothing new has been committed\nfor the chunk yet, which is normal early on.\n\nSegment times inside `alignment` are relative to the start of their chunk.\n`chunk_audio_offset_sec` is the chunk's own start time within the full audio,\nso the absolute time of a word is `chunk_audio_offset_sec + segment.start`.\nBoth `chunk_seq` and `chunk_audio_offset_sec` are monotonically\nnon-decreasing. A frame may be labelled with the previous chunk's\n`chunk_seq` while carrying the next chunk's audio bytes, so never use\n`chunk_seq` to attribute audio bytes — only alignment.", - "security": [ - { - "BearerAuth": [] - } - ], - "parameters": [ - { - "in": "header", - "name": "model", - "description": "Specify which TTS model to use. Use `s2.1-pro-free` for the free developer tier. `drama-3-preview` is a preview model; its behavior and availability may change. If omitted or set to an unrecognized value, the request falls back to `s2.1-pro`.", - "required": false, - "schema": { - "default": "s2.1-pro", - "enum": [ - "s1", - "s2-pro", - "s2.1-pro", - "s2.1-pro-free", - "drama-3-preview" - ], - "title": "Model", - "type": "string" - }, - "deprecated": false - } - ], - "responses": { - "200": { - "description": "WebSocket handshake accepted (the server answers `101 Switching Protocols` on the wire). Every frame the server then sends is a MessagePack-encoded binary frame holding one of the events below. `audio` events carry the audio chunk together with `alignment`, `chunk_seq` and `chunk_audio_offset_sec`; `alignment` is the cumulative snapshot for its `chunk_seq` and replaces the previous one rather than extending it. Segment times are relative to the chunk, so absolute time is `chunk_audio_offset_sec + segment.start`. The stream ends with a `finish` event.", - "content": { - "application/msgpack": { - "schema": { - "discriminator": { - "mapping": { - "audio": "#/components/schemas/TTSLiveWithTimestampAudioEvent", - "error": "#/components/schemas/TTSLiveErrorEvent", - "finish": "#/components/schemas/TTSLiveFinishEvent" + "title": "Type", + "type": "string" }, - "propertyName": "event" - }, - "oneOf": [ - { - "$ref": "#/components/schemas/TTSLiveWithTimestampAudioEvent" + "title": { + "title": "Title", + "type": "string" }, - { - "$ref": "#/components/schemas/TTSLiveFinishEvent" + "description": { + "default": "", + "title": "Description", + "type": "string" }, - { - "$ref": "#/components/schemas/TTSLiveErrorEvent" - } - ] - } - } - } - }, - "401": { - "description": "No permission -- see authorization schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" + "cover_image": { + "default": "", + "title": "Cover Image", + "type": "string" }, - "message": { - "title": "Message", + "train_mode": { + "default": "full", + "enum": [ + "fast", + "full" + ], + "title": "Train Mode", "type": "string" }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } + "state": { + "enum": [ + "created", + "training", + "trained", + "failed" ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - }, - "402": { - "description": "No payment -- see charging schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" + "title": "State", + "type": "string" }, - "message": { - "title": "Message", + "tags": { + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "samples": { + "default": [], + "items": { + "$ref": "#/components/schemas/SampleEntity" + }, + "title": "Samples", + "type": "array" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "languages": { + "default": [], + "items": { + "type": "string" + }, + "title": "Languages", + "type": "array" + }, + "visibility": { + "enum": [ + "public", + "unlist", + "private" + ], + "title": "Visibility", "type": "string" }, - "reason": { + "lock_visibility": { + "default": false, + "title": "Lock Visibility", + "type": "boolean" + }, + "dmca_taken_down": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "title": "Dmca Taken Down" + }, + "takedown_category": { "anyOf": [ { + "enum": [ + "dmca", + "policy" + ], "type": "string" }, { @@ -11935,34 +16772,14 @@ } ], "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - }, - "503": { - "description": "The server cannot process the request due to a high load", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" + "title": "Takedown Category" }, - "message": { - "title": "Message", + "default_text": { + "default": "", + "title": "Default Text", "type": "string" }, - "reason": { + "source": { "anyOf": [ { "type": "string" @@ -11972,139 +16789,20 @@ } ], "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - } - }, - "tags": [ - "OpenAPI v1" - ], - "x-codeSamples": [ - { - "lang": "python", - "label": "Live WebSocket With Timestamps", - "source": "import asyncio\n\nimport ormsgpack\nimport websockets\n\nTEXT = \"Hello there. This is a live synthesis, streamed word by word.\"\n\n\nasync def send_text(socket) -> None:\n # `request` takes the same fields as POST /v1/tts. Leave `text` empty and\n # stream the words in as they become available.\n await socket.send(\n ormsgpack.packb(\n {\n \"event\": \"start\",\n \"request\": {\n \"text\": \"\",\n \"format\": \"opus\",\n \"latency\": \"balanced\",\n \"chunk_length\": 100,\n \"reference_id\": \"fbe02f8306fc4d3d915e9871722a39d5\",\n },\n }\n )\n )\n for word in TEXT.split(\" \"):\n await socket.send(ormsgpack.packb({\"event\": \"text\", \"text\": word + \" \"}))\n await socket.send(ormsgpack.packb({\"event\": \"stop\"}))\n\n\nasync def main() -> None:\n async with websockets.connect(\n \"wss://api.fish.audio/v1/tts/live/with-timestamp\",\n additional_headers={\n \"Authorization\": \"Bearer \",\n \"model\": \"s2.1-pro-free\",\n },\n max_size=None,\n ) as socket:\n # Send and receive concurrently. Sending everything before reading can\n # stall on flow control once the audio outpaces your receive buffer.\n sender = asyncio.create_task(send_text(socket))\n\n audio = bytearray()\n alignments: dict[int, dict] = {}\n offsets: dict[int, float] = {}\n\n try:\n async for frame in socket:\n if isinstance(frame, str):\n continue\n message = ormsgpack.unpackb(frame)\n if message[\"event\"] == \"audio\":\n audio += message[\"audio\"]\n offsets[message[\"chunk_seq\"]] = message[\"chunk_audio_offset_sec\"]\n if message[\"alignment\"] is not None:\n # Cumulative snapshot: replace the chunk, never append.\n alignments[message[\"chunk_seq\"]] = message[\"alignment\"]\n elif message[\"event\"] == \"finish\":\n if message[\"reason\"] == \"error\":\n raise RuntimeError(message.get(\"message\", \"synthesis failed\"))\n break\n elif message[\"event\"] == \"error\":\n raise RuntimeError(message[\"error\"])\n finally:\n sender.cancel()\n\n with open(\"output.opus\", \"wb\") as f:\n f.write(audio)\n\n # Segment times are chunk-relative; fold in the offset to get absolute.\n for chunk_seq in sorted(alignments):\n offset = offsets[chunk_seq]\n for segment in alignments[chunk_seq][\"segments\"]:\n start = offset + segment[\"start\"]\n end = offset + segment[\"end\"]\n print(f\"{start:6.2f}s -> {end:6.2f}s {segment['text']}\")\n\n\nasyncio.run(main())" - } - ], - "x-fish-websocket": { - "url": "wss://api.fish.audio/v1/tts/live/with-timestamp", - "encoding": "application/msgpack", - "clientEvents": { - "description": "Frames the client sends, MessagePack encoded. Identical to the `/v1/tts/live` inbound protocol: `start` once, then any number of `text` events, optional `flush` events, and a final `stop`.", - "schema": { - "discriminator": { - "mapping": { - "flush": "#/components/schemas/TTSLiveFlushEvent", - "start": "#/components/schemas/TTSLiveStartEvent", - "stop": "#/components/schemas/TTSLiveStopEvent", - "text": "#/components/schemas/TTSLiveTextEvent" - }, - "propertyName": "event" - }, - "oneOf": [ - { - "$ref": "#/components/schemas/TTSLiveStartEvent" - }, - { - "$ref": "#/components/schemas/TTSLiveTextEvent" - }, - { - "$ref": "#/components/schemas/TTSLiveFlushEvent" - }, - { - "$ref": "#/components/schemas/TTSLiveStopEvent" - } - ] - } - } - } - } - }, - "/v1/voice-design": { - "post": { - "summary": "Voice Design", - "security": [ - { - "BearerAuth": [] - } - ], - "parameters": [ - { - "in": "header", - "name": "model", - "description": "Specify which voice-design model to use.", - "required": true, - "schema": { - "const": "voice-design-1", - "default": "voice-design-1", - "title": "Model", - "type": "string" - }, - "deprecated": false - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VoiceDesignRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "candidates": { - "description": "Generated voice candidates.", - "items": { - "$ref": "#/components/schemas/VoiceDesignCandidate" - }, - "title": "Candidates", - "type": "array" - } - }, - "required": [ - "candidates" - ], - "type": "object" - } - } - } - }, - "401": { - "description": "No permission -- see authorization schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" + "title": "Source" }, - "message": { - "title": "Message", - "type": "string" + "licensed": { + "default": false, + "title": "Licensed", + "type": "boolean" }, - "reason": { + "pvc_release_state": { "anyOf": [ { + "enum": [ + "released", + "retiring" + ], "type": "string" }, { @@ -12112,36 +16810,24 @@ } ], "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - }, - "402": { - "description": "No payment -- see charging schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" + "title": "Pvc Release State" }, - "message": { - "title": "Message", - "type": "string" + "pvc_notice_period_months": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pvc Notice Period Months" }, - "reason": { + "pvc_released_at": { "anyOf": [ { + "format": "date-time", "type": "string" }, { @@ -12149,36 +16835,25 @@ } ], "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - }, - "503": { - "description": "The server cannot process the request due to a high load", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" + "title": "Pvc Released At" }, - "message": { - "title": "Message", - "type": "string" + "pvc_retire_requested_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pvc Retire Requested At" }, - "reason": { + "pvc_retire_effective_at": { "anyOf": [ { + "format": "date-time", "type": "string" }, { @@ -12186,806 +16861,564 @@ } ], "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - } - }, - "tags": [ - "OpenAPI v1" - ], - "x-codeSamples": [ - { - "lang": "bash", - "label": "Voice Design", - "source": "curl --request POST \\\n --url https://api.fish.audio/v1/voice-design \\\n --header 'Authorization: Bearer ' \\\n --header 'Content-Type: application/json' \\\n --header 'model: voice-design-1' \\\n --data '{\n \"instruction\": \"Warm, confident studio narrator with a natural tone\",\n \"reference_text\": \"Welcome to Fish Audio.\",\n \"language\": \"en\",\n \"n\": 2,\n \"speed\": 1,\n \"num_step\": 32,\n \"guidance_scale\": 2,\n \"instruct_guidance_scale\": 0,\n \"seed\": 42\n }'" - } - ] - } - }, - "/model": { - "get": { - "summary": "List Models", - "security": [ - { - "BearerAuth": [] - } - ], - "parameters": [ - { - "in": "query", - "name": "page_size", - "description": "Page size", - "required": false, - "schema": { - "default": 10, - "maximum": 100, - "minimum": 1, - "title": "Page Size", - "type": "integer" - }, - "deprecated": false - }, - { - "in": "query", - "name": "page_number", - "description": "Page number", - "required": false, - "schema": { - "default": 1, - "minimum": 1, - "title": "Page Number", - "type": "integer" - }, - "deprecated": false - }, - { - "in": "query", - "name": "title", - "description": "Title to filter models", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Title" - }, - "deprecated": false - }, - { - "in": "query", - "name": "tag", - "description": "Tag to filter models", - "required": false, - "schema": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Tag" - }, - "deprecated": false - }, - { - "in": "query", - "name": "self", - "description": "If True, return models owned by the active workspace", - "required": false, - "schema": { - "default": false, - "title": "Self", - "type": "boolean" - }, - "deprecated": false - }, - { - "in": "query", - "name": "author_id", - "description": "Author ID to filter public models; ignored if self is True", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Author Id" - }, - "deprecated": false - }, - { - "in": "query", - "name": "language", - "description": "Language to filter models", - "required": false, - "schema": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Language" - }, - "deprecated": false - }, - { - "in": "query", - "name": "title_language", - "description": "Title language to filter models", - "required": false, - "schema": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Title Language" - }, - "deprecated": false - }, - { - "in": "query", - "name": "licensed", - "description": "If True, only voices licensed by Fish Audio (rights secured from the voice owner) are returned; ignored if self is True", - "required": false, - "schema": { - "default": false, - "title": "Licensed", - "type": "boolean" - }, - "deprecated": false - }, - { - "in": "query", - "name": "sort_by", - "description": "", - "required": false, - "schema": { - "default": "score", - "enum": [ - "score", - "task_count", - "created_at" - ], - "title": "Sort By", - "type": "string" - }, - "deprecated": false - } - ], - "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "max_offset": { + "title": "Pvc Retire Effective At" + }, + "quality": { "anyOf": [ { - "type": "integer" + "$ref": "#/components/schemas/ModelQualityEntity" }, { "type": "null" } ], - "default": null, - "title": "Max Offset" + "default": null + }, + "like_count": { + "title": "Like Count", + "type": "integer" + }, + "mark_count": { + "title": "Mark Count", + "type": "integer" + }, + "shared_count": { + "title": "Shared Count", + "type": "integer" + }, + "task_count": { + "title": "Task Count", + "type": "integer" }, - "accessible_upper_bound": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Accessible Upper Bound" + "unliked": { + "default": false, + "title": "Unliked", + "type": "boolean" }, - "window_limited": { + "liked": { "default": false, - "title": "Window Limited", + "title": "Liked", "type": "boolean" }, - "total_is_exact": { - "default": true, - "title": "Total Is Exact", + "marked": { + "default": false, + "title": "Marked", "type": "boolean" }, - "total": { - "title": "Total", + "author": { + "$ref": "#/components/schemas/AuthorEntity" + } + }, + "required": [ + "_id", + "type", + "title", + "state", + "tags", + "created_at", + "updated_at", + "visibility", + "like_count", + "mark_count", + "shared_count", + "task_count", + "author" + ], + "type": "object" + } + } + } + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", "type": "integer" }, - "items": { - "items": { - "$ref": "#/components/schemas/ModelEntity" - }, - "title": "Items", - "type": "array" + "message": { + "title": "Message", + "type": "string" }, - "has_more": { + "reason": { "anyOf": [ { - "type": "boolean" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Has More" + "title": "Reason" } }, "required": [ - "total", - "items" + "status", + "message" ], "type": "object" } } } - } - }, - "tags": [ - "Model" - ] - }, - "post": { - "summary": "Create Model for Users via API", - "security": [ - { - "BearerAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "visibility": { - "default": "private", - "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", - "enum": [ - "public", - "unlist", - "private" - ], - "title": "Visibility", - "type": "string" - }, - "type": { - "const": "tts", - "description": "Model type, tts is for text to speech", - "title": "Type", - "type": "string" - }, - "title": { - "description": "Model title or name", - "title": "Title", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Model description", - "title": "Description" - }, - "cover_image": { - "anyOf": [ - { - "format": "binary", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Model cover image, this is required if the model is public", - "title": "Cover Image" - }, - "train_mode": { - "const": "fast", - "description": "Model train mode, for TTS model, fast means model instantly available after creation", - "title": "Train Mode", - "type": "string" - }, - "voices": { - "anyOf": [ - { - "items": { - "format": "binary", - "type": "string" - }, - "maxItems": 20, - "minItems": 1, - "type": "array" - }, - { - "format": "binary", - "type": "string" - } - ], - "description": "Upload voices files that will be used to tune the model", - "title": "Voices" - }, - "texts": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", - "title": "Texts" - }, - "voice_design_signatures": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", - "title": "Voice Design Signatures" - }, - "tags": { - "anyOf": [ - { - "items": { + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { "type": "string" }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Model tags", - "title": "Tags" - }, - "enhance_audio_quality": { - "default": true, - "description": "Enhance audio quality", - "title": "Enhance Audio Quality", - "type": "boolean" + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } }, - "generate_sample": { - "default": false, - "description": "Generate default text", - "title": "Generate Sample", - "type": "boolean" - } - }, - "required": [ - "type", - "title", - "train_mode", - "voices" - ], - "type": "object" + "required": [ + "status", + "message" + ], + "type": "object" + } } + } + } + }, + "tags": [ + "Model" + ] + } + }, + "/model/{id}": { + "get": { + "summary": "Get Model", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "", + "required": true, + "schema": { + "title": "Id", + "type": "string" }, - "application/x-www-form-urlencoded": { - "schema": { - "properties": { - "visibility": { - "default": "private", - "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", - "enum": [ - "public", - "unlist", - "private" - ], - "title": "Visibility", - "type": "string" - }, - "type": { - "const": "tts", - "description": "Model type, tts is for text to speech", - "title": "Type", - "type": "string" - }, - "title": { - "description": "Model title or name", - "title": "Title", - "type": "string" - }, - "description": { - "anyOf": [ - { + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "_id": { + "title": "Id", + "type": "string" + }, + "type": { + "enum": [ + "svc", + "tts" + ], + "title": "Type", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "description": { + "default": "", + "title": "Description", + "type": "string" + }, + "cover_image": { + "default": "", + "title": "Cover Image", + "type": "string" + }, + "train_mode": { + "default": "full", + "enum": [ + "fast", + "full" + ], + "title": "Train Mode", + "type": "string" + }, + "state": { + "enum": [ + "created", + "training", + "trained", + "failed" + ], + "title": "State", + "type": "string" + }, + "tags": { + "items": { "type": "string" }, - { - "type": "null" - } - ], - "default": null, - "description": "Model description", - "title": "Description" - }, - "cover_image": { - "anyOf": [ - { - "format": "binary", + "title": "Tags", + "type": "array" + }, + "samples": { + "default": [], + "items": { + "$ref": "#/components/schemas/SampleEntity" + }, + "title": "Samples", + "type": "array" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "languages": { + "default": [], + "items": { "type": "string" }, - { - "type": "null" - } - ], - "default": null, - "description": "Model cover image, this is required if the model is public", - "title": "Cover Image" - }, - "train_mode": { - "const": "fast", - "description": "Model train mode, for TTS model, fast means model instantly available after creation", - "title": "Train Mode", - "type": "string" - }, - "voices": { - "anyOf": [ - { - "items": { - "format": "binary", + "title": "Languages", + "type": "array" + }, + "visibility": { + "enum": [ + "public", + "unlist", + "private" + ], + "title": "Visibility", + "type": "string" + }, + "lock_visibility": { + "default": false, + "title": "Lock Visibility", + "type": "boolean" + }, + "dmca_taken_down": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "title": "Dmca Taken Down" + }, + "takedown_category": { + "anyOf": [ + { + "enum": [ + "dmca", + "policy" + ], "type": "string" }, - "maxItems": 20, - "minItems": 1, - "type": "array" - }, - { - "format": "binary", - "type": "string" - } - ], - "description": "Upload voices files that will be used to tune the model", - "title": "Voices" - }, - "texts": { - "anyOf": [ - { - "items": { + { + "type": "null" + } + ], + "default": null, + "title": "Takedown Category" + }, + "default_text": { + "default": "", + "title": "Default Text", + "type": "string" + }, + "source": { + "anyOf": [ + { "type": "string" }, - "maxItems": 20, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", - "title": "Texts" - }, - "voice_design_signatures": { - "anyOf": [ - { - "items": { + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "licensed": { + "default": false, + "title": "Licensed", + "type": "boolean" + }, + "pvc_release_state": { + "anyOf": [ + { + "enum": [ + "released", + "retiring" + ], "type": "string" }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", - "title": "Voice Design Signatures" - }, - "tags": { - "anyOf": [ - { - "items": { + { + "type": "null" + } + ], + "default": null, + "title": "Pvc Release State" + }, + "pvc_notice_period_months": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pvc Notice Period Months" + }, + "pvc_released_at": { + "anyOf": [ + { + "format": "date-time", "type": "string" }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Model tags", - "title": "Tags" - }, - "enhance_audio_quality": { - "default": true, - "description": "Enhance audio quality", - "title": "Enhance Audio Quality", - "type": "boolean" - }, - "generate_sample": { - "default": false, - "description": "Generate default text", - "title": "Generate Sample", - "type": "boolean" - } - }, - "required": [ - "type", - "title", - "train_mode", - "voices" - ], - "type": "object" - } - }, - "multipart/form-data": { - "schema": { - "properties": { - "visibility": { - "default": "private", - "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", - "enum": [ - "public", - "unlist", - "private" - ], - "title": "Visibility", - "type": "string" - }, - "type": { - "const": "tts", - "description": "Model type, tts is for text to speech", - "title": "Type", - "type": "string" - }, - "title": { - "description": "Model title or name", - "title": "Title", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Model description", - "title": "Description" - }, - "cover_image": { - "anyOf": [ - { - "format": "binary", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Model cover image, this is required if the model is public", - "title": "Cover Image" - }, - "train_mode": { - "const": "fast", - "description": "Model train mode, for TTS model, fast means model instantly available after creation", - "title": "Train Mode", - "type": "string" - }, - "voices": { - "anyOf": [ - { - "items": { - "format": "binary", + { + "type": "null" + } + ], + "default": null, + "title": "Pvc Released At" + }, + "pvc_retire_requested_at": { + "anyOf": [ + { + "format": "date-time", "type": "string" }, - "maxItems": 20, - "minItems": 1, - "type": "array" - }, - { - "format": "binary", - "type": "string" - } - ], - "description": "Upload voices files that will be used to tune the model", - "title": "Voices" - }, - "texts": { - "anyOf": [ - { - "items": { + { + "type": "null" + } + ], + "default": null, + "title": "Pvc Retire Requested At" + }, + "pvc_retire_effective_at": { + "anyOf": [ + { + "format": "date-time", "type": "string" }, - "maxItems": 20, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", - "title": "Texts" + { + "type": "null" + } + ], + "default": null, + "title": "Pvc Retire Effective At" + }, + "quality": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelQualityEntity" + }, + { + "type": "null" + } + ], + "default": null + }, + "like_count": { + "title": "Like Count", + "type": "integer" + }, + "mark_count": { + "title": "Mark Count", + "type": "integer" + }, + "shared_count": { + "title": "Shared Count", + "type": "integer" + }, + "task_count": { + "title": "Task Count", + "type": "integer" + }, + "unliked": { + "default": false, + "title": "Unliked", + "type": "boolean" + }, + "liked": { + "default": false, + "title": "Liked", + "type": "boolean" + }, + "marked": { + "default": false, + "title": "Marked", + "type": "boolean" + }, + "author": { + "$ref": "#/components/schemas/AuthorEntity" + } }, - "voice_design_signatures": { - "anyOf": [ - { - "items": { + "required": [ + "_id", + "type", + "title", + "state", + "tags", + "created_at", + "updated_at", + "visibility", + "like_count", + "mark_count", + "shared_count", + "task_count", + "author" + ], + "type": "object" + } + } + } + }, + "403": { + "description": "Request forbidden -- authorization will not help", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { "type": "string" }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", - "title": "Voice Design Signatures" + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } }, - "tags": { - "anyOf": [ - { - "items": { + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "Nothing matches the given URI", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" + }, + "message": { + "title": "Message", + "type": "string" + }, + "reason": { + "anyOf": [ + { "type": "string" }, - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Model tags", - "title": "Tags" - }, - "enhance_audio_quality": { - "default": true, - "description": "Enhance audio quality", - "title": "Enhance Audio Quality", - "type": "boolean" + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } }, - "generate_sample": { - "default": false, - "description": "Generate default text", - "title": "Generate Sample", - "type": "boolean" - } - }, - "required": [ - "type", - "title", - "train_mode", - "voices" - ], - "type": "object" + "required": [ + "status", + "message" + ], + "type": "object" + } } + } + } + }, + "tags": [ + "Model" + ] + }, + "patch": { + "summary": "Update Model", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "", + "required": true, + "schema": { + "title": "Id", + "type": "string" }, - "application/msgpack": { + "deprecated": false + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "properties": { - "visibility": { - "default": "private", - "description": "Model visibility. Public requests are downgraded to private; use the web publish flow to publish publicly. Unlist allows anyone with the link to access, while private is visible only to the creator.", - "enum": [ - "public", - "unlist", - "private" - ], - "title": "Visibility", - "type": "string" - }, - "type": { - "const": "tts", - "description": "Model type, tts is for text to speech", - "title": "Type", - "type": "string" - }, "title": { - "description": "Model title or name", - "title": "Title", - "type": "string" - }, - "description": { "anyOf": [ { "type": "string" @@ -12995,13 +17428,11 @@ } ], "default": null, - "description": "Model description", - "title": "Description" + "title": "Title" }, - "cover_image": { + "description": { "anyOf": [ { - "format": "binary", "type": "string" }, { @@ -13009,44 +17440,12 @@ } ], "default": null, - "description": "Model cover image, this is required if the model is public", - "title": "Cover Image" - }, - "train_mode": { - "const": "fast", - "description": "Model train mode, for TTS model, fast means model instantly available after creation", - "title": "Train Mode", - "type": "string" - }, - "voices": { - "anyOf": [ - { - "items": { - "format": "binary", - "type": "string" - }, - "maxItems": 20, - "minItems": 1, - "type": "array" - }, - { - "format": "binary", - "type": "string" - } - ], - "description": "Upload voices files that will be used to tune the model", - "title": "Voices" - }, - "texts": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, + "title": "Description" + }, + "cover_image": { + "anyOf": [ { + "format": "binary", "type": "string" }, { @@ -13054,18 +17453,16 @@ } ], "default": null, - "description": "Texts corresponding to the voices, if unspecified, ASR will be performed on the voices", - "title": "Texts" + "title": "Cover Image" }, - "voice_design_signatures": { + "visibility": { "anyOf": [ { - "items": { - "type": "string" - }, - "type": "array" - }, - { + "enum": [ + "public", + "unlist", + "private" + ], "type": "string" }, { @@ -13073,8 +17470,7 @@ } ], "default": null, - "description": "Signatures from /v1/voice-design candidates, one per voice in the same order. When every uploaded voice verifies, the model is stamped source=voice_design; an invalid signature rejects the request.", - "title": "Voice Design Signatures" + "title": "Visibility" }, "tags": { "anyOf": [ @@ -13086,320 +17482,245 @@ }, { "type": "string" - }, - { - "type": "null" } ], - "description": "Model tags", "title": "Tags" - }, - "enhance_audio_quality": { - "default": true, - "description": "Enhance audio quality", - "title": "Enhance Audio Quality", - "type": "boolean" - }, - "generate_sample": { - "default": false, - "description": "Generate default text", - "title": "Generate Sample", - "type": "boolean" } }, - "required": [ - "type", - "title", - "train_mode", - "voices" - ], "type": "object" } - } - } - }, - "responses": { - "201": { - "description": "Document created, URL follows", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "_id": { - "title": "Id", - "type": "string" - }, - "type": { - "enum": [ - "svc", - "tts" - ], - "title": "Type", - "type": "string" - }, - "title": { - "title": "Title", - "type": "string" - }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "cover_image": { - "default": "", - "title": "Cover Image", - "type": "string" - }, - "train_mode": { - "default": "full", - "enum": [ - "fast", - "full" - ], - "title": "Train Mode", - "type": "string" - }, - "state": { - "enum": [ - "created", - "training", - "trained", - "failed" - ], - "title": "State", - "type": "string" - }, - "tags": { - "items": { + }, + "application/x-www-form-urlencoded": { + "schema": { + "properties": { + "title": { + "anyOf": [ + { "type": "string" }, - "title": "Tags", - "type": "array" - }, - "samples": { - "default": [], - "items": { - "$ref": "#/components/schemas/SampleEntity" - }, - "title": "Samples", - "type": "array" - }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" - }, - "languages": { - "default": [], - "items": { + { + "type": "null" + } + ], + "default": null, + "title": "Title" + }, + "description": { + "anyOf": [ + { "type": "string" }, - "title": "Languages", - "type": "array" - }, - "visibility": { - "enum": [ - "public", - "unlist", - "private" - ], - "title": "Visibility", - "type": "string" - }, - "lock_visibility": { - "default": false, - "title": "Lock Visibility", - "type": "boolean" - }, - "dmca_taken_down": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": false, - "title": "Dmca Taken Down" - }, - "takedown_category": { - "anyOf": [ - { - "enum": [ - "dmca", - "policy" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Takedown Category" - }, - "default_text": { - "default": "", - "title": "Default Text", - "type": "string" - }, - "source": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Source" - }, - "licensed": { - "default": false, - "title": "Licensed", - "type": "boolean" - }, - "pvc_release_state": { - "anyOf": [ - { - "enum": [ - "released", - "retiring" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Pvc Release State" - }, - "pvc_notice_period_months": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Pvc Notice Period Months" - }, - "pvc_released_at": { - "anyOf": [ - { - "format": "date-time", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Pvc Released At" - }, - "pvc_retire_requested_at": { - "anyOf": [ - { - "format": "date-time", + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "cover_image": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cover Image" + }, + "visibility": { + "anyOf": [ + { + "enum": [ + "public", + "unlist", + "private" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Visibility" + }, + "tags": { + "anyOf": [ + { + "items": { "type": "string" }, - { - "type": "null" - } - ], - "default": null, - "title": "Pvc Retire Requested At" - }, - "pvc_retire_effective_at": { - "anyOf": [ - { - "format": "date-time", + "type": "array" + }, + { + "type": "string" + } + ], + "title": "Tags" + } + }, + "type": "object" + } + }, + "multipart/form-data": { + "schema": { + "properties": { + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Title" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "cover_image": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cover Image" + }, + "visibility": { + "anyOf": [ + { + "enum": [ + "public", + "unlist", + "private" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Visibility" + }, + "tags": { + "anyOf": [ + { + "items": { "type": "string" }, - { - "type": "null" - } - ], - "default": null, - "title": "Pvc Retire Effective At" - }, - "quality": { - "anyOf": [ - { - "$ref": "#/components/schemas/ModelQualityEntity" - }, - { - "type": "null" - } - ], - "default": null - }, - "like_count": { - "title": "Like Count", - "type": "integer" - }, - "mark_count": { - "title": "Mark Count", - "type": "integer" - }, - "shared_count": { - "title": "Shared Count", - "type": "integer" - }, - "task_count": { - "title": "Task Count", - "type": "integer" - }, - "unliked": { - "default": false, - "title": "Unliked", - "type": "boolean" - }, - "liked": { - "default": false, - "title": "Liked", - "type": "boolean" - }, - "marked": { - "default": false, - "title": "Marked", - "type": "boolean" - }, - "author": { - "$ref": "#/components/schemas/AuthorEntity" - } + "type": "array" + }, + { + "type": "string" + } + ], + "title": "Tags" + } + }, + "type": "object" + } + }, + "application/msgpack": { + "schema": { + "properties": { + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Title" }, - "required": [ - "_id", - "type", - "title", - "state", - "tags", - "created_at", - "updated_at", - "visibility", - "like_count", - "mark_count", - "shared_count", - "task_count", - "author" - ], - "type": "object" - } + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "cover_image": { + "anyOf": [ + { + "format": "binary", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cover Image" + }, + "visibility": { + "anyOf": [ + { + "enum": [ + "public", + "unlist", + "private" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Visibility" + }, + "tags": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "title": "Tags" + } + }, + "type": "object" } } + } + }, + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {} }, "401": { "description": "No permission -- see authorization schemes", @@ -13453,237 +17774,9 @@ "title": "Message", "type": "string" }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } - } - } - } - }, - "tags": [ - "Model" - ] - } - }, - "/model/{id}": { - "get": { - "summary": "Get Model", - "security": [ - { - "BearerAuth": [] - } - ], - "parameters": [ - { - "in": "path", - "name": "id", - "description": "", - "required": true, - "schema": { - "title": "Id", - "type": "string" - }, - "deprecated": false - } - ], - "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "_id": { - "title": "Id", - "type": "string" - }, - "type": { - "enum": [ - "svc", - "tts" - ], - "title": "Type", - "type": "string" - }, - "title": { - "title": "Title", - "type": "string" - }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "cover_image": { - "default": "", - "title": "Cover Image", - "type": "string" - }, - "train_mode": { - "default": "full", - "enum": [ - "fast", - "full" - ], - "title": "Train Mode", - "type": "string" - }, - "state": { - "enum": [ - "created", - "training", - "trained", - "failed" - ], - "title": "State", - "type": "string" - }, - "tags": { - "items": { - "type": "string" - }, - "title": "Tags", - "type": "array" - }, - "samples": { - "default": [], - "items": { - "$ref": "#/components/schemas/SampleEntity" - }, - "title": "Samples", - "type": "array" - }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" - }, - "languages": { - "default": [], - "items": { - "type": "string" - }, - "title": "Languages", - "type": "array" - }, - "visibility": { - "enum": [ - "public", - "unlist", - "private" - ], - "title": "Visibility", - "type": "string" - }, - "lock_visibility": { - "default": false, - "title": "Lock Visibility", - "type": "boolean" - }, - "dmca_taken_down": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": false, - "title": "Dmca Taken Down" - }, - "takedown_category": { - "anyOf": [ - { - "enum": [ - "dmca", - "policy" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Takedown Category" - }, - "default_text": { - "default": "", - "title": "Default Text", - "type": "string" - }, - "source": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Source" - }, - "licensed": { - "default": false, - "title": "Licensed", - "type": "boolean" - }, - "pvc_release_state": { - "anyOf": [ - { - "enum": [ - "released", - "retiring" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Pvc Release State" - }, - "pvc_notice_period_months": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Pvc Notice Period Months" - }, - "pvc_released_at": { + "reason": { "anyOf": [ { - "format": "date-time", "type": "string" }, { @@ -13691,12 +17784,66 @@ } ], "default": null, - "title": "Pvc Released At" + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + } + }, + "tags": [ + "Model" + ] + }, + "delete": { + "summary": "Delete Model", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "", + "required": true, + "schema": { + "title": "Id", + "type": "string" + }, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "Request fulfilled, document follows", + "headers": {} + }, + "401": { + "description": "No permission -- see authorization schemes", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "pvc_retire_requested_at": { + "message": { + "title": "Message", + "type": "string" + }, + "reason": { "anyOf": [ { - "format": "date-time", "type": "string" }, { @@ -13704,12 +17851,36 @@ } ], "default": null, - "title": "Pvc Retire Requested At" + "title": "Reason" + } + }, + "required": [ + "status", + "message" + ], + "type": "object" + } + } + } + }, + "503": { + "description": "The server cannot process the request due to a high load", + "headers": {}, + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "title": "Status", + "type": "integer" }, - "pvc_retire_effective_at": { + "message": { + "title": "Message", + "type": "string" + }, + "reason": { "anyOf": [ { - "format": "date-time", "type": "string" }, { @@ -13717,693 +17888,888 @@ } ], "default": null, - "title": "Pvc Retire Effective At" - }, - "quality": { - "anyOf": [ - { - "$ref": "#/components/schemas/ModelQualityEntity" - }, - { - "type": "null" - } - ], - "default": null - }, - "like_count": { - "title": "Like Count", - "type": "integer" - }, - "mark_count": { - "title": "Mark Count", - "type": "integer" - }, - "shared_count": { - "title": "Shared Count", - "type": "integer" - }, - "task_count": { - "title": "Task Count", - "type": "integer" - }, - "unliked": { - "default": false, - "title": "Unliked", - "type": "boolean" - }, - "liked": { - "default": false, - "title": "Liked", - "type": "boolean" - }, - "marked": { - "default": false, - "title": "Marked", - "type": "boolean" - }, - "author": { - "$ref": "#/components/schemas/AuthorEntity" + "title": "Reason" } }, "required": [ - "_id", - "type", - "title", - "state", - "tags", - "created_at", - "updated_at", - "visibility", - "like_count", - "mark_count", - "shared_count", - "task_count", - "author" + "status", + "message" ], "type": "object" } } - } + } + } + }, + "tags": [ + "Model" + ] + } + } + }, + "tags": [], + "components": { + "securitySchemes": { + "BearerAuth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "AgentSessionSummary": { + "description": "One list row: lifecycle facts only — the conversation timeline lives on\nthe detail endpoint (Retell-style thin list).", + "properties": { + "session_id": { + "title": "Session Id", + "type": "string" + }, + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "agent_name": { + "title": "Agent Name", + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "status": { + "enum": [ + "pending", + "active", + "completed", + "failed", + "unknown" + ], + "title": "Status", + "type": "string" + }, + "end_reason": { + "anyOf": [ + { + "enum": [ + "user_hangup", + "agent_hangup", + "escalated", + "conversation_timeout", + "heartbeat_timeout", + "agent_join_timeout", + "internal_error", + "llm_endpoint_failure", + "room_join_timeout", + "cancelled", + "dial_failed" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "End Reason" + }, + "source": { + "enum": [ + "api_key", + "dashboard", + "public", + "phone" + ], + "title": "Source", + "type": "string" + }, + "direction": { + "default": "inbound", + "enum": [ + "inbound", + "outbound" + ], + "title": "Direction", + "type": "string" + }, + "dial_status": { + "anyOf": [ + { + "enum": [ + "answered", + "busy", + "no_answer", + "failed" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Dial Status" }, - "403": { - "description": "Request forbidden -- authorization will not help", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } + "answered_by": { + "anyOf": [ + { + "enum": [ + "human", + "voicemail", + "unknown" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Answered By" + }, + "caller_number": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Caller Number" + }, + "dialed_number": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Dialed Number" + }, + "sip_call_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sip Call Id" + }, + "timezone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Timezone" + }, + "created_at": { + "title": "Created At", + "type": "string" + }, + "started_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Started At" + }, + "ended_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ended At" + }, + "duration_seconds": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Duration Seconds" + }, + "participant_identity": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Participant Identity" + }, + "agent_participant_identity": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Participant Identity" + }, + "agent_joined_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Joined At" + }, + "metadata": { + "additionalProperties": { + "$ref": "#/components/schemas/JsonValue" + }, + "title": "Metadata", + "type": "object" + } + }, + "required": [ + "session_id", + "agent_id", + "agent_name", + "status", + "source", + "created_at" + ], + "title": "AgentSessionSummary", + "type": "object" + }, + "JsonValue": {}, + "AgentSessionCreatePayload": { + "additionalProperties": false, + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "name": { + "anyOf": [ + { + "maxLength": 128, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "timezone": { + "anyOf": [ + { + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "IANA timezone (like `Asia/Tokyo`) for the agent's sense of local date and time in this session. Overrides the agent's configured timezone, which defaults to UTC. Invalid names are rejected with 422.", + "title": "Timezone" + }, + "client_timezone": { + "anyOf": [ + { + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The end user's device timezone as an IANA name (like `Asia/Tokyo`), used as a fallback hint: it applies only when neither `timezone` nor the agent's configured timezone is set, and invalid values are ignored rather than rejected. The Web SDK fills it automatically from the browser for public agents. When your backend creates the session, forward the value from your client.", + "title": "Client Timezone" + }, + "world_context": { + "default": true, + "title": "World Context", + "type": "boolean" + }, + "overrides": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentSessionOverridesPayload" + }, + { + "type": "null" } - } + ], + "default": null }, - "404": { - "description": "Nothing matches the given URI", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", + "dynamic_variables": { + "anyOf": [ + { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { "type": "integer" }, - "message": { - "title": "Message", - "type": "string" + { + "type": "number" }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" + { + "type": "boolean" } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } + ] + }, + "maxProperties": 50, + "type": "object" + }, + { + "type": "null" } - } + ], + "default": null, + "title": "Dynamic Variables" + }, + "end_user_id": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "End User Id" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/components/schemas/JsonValue" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Metadata" + }, + "llm_extra_body": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/components/schemas/JsonValue" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Llm Extra Body" + }, + "tool_events": { + "default": true, + "title": "Tool Events", + "type": "boolean" + }, + "record_audio": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Record Audio" + } + }, + "required": [ + "agent_id" + ], + "title": "AgentSessionCreatePayload", + "type": "object" + }, + "AgentSessionOverridesPayload": { + "additionalProperties": false, + "description": "Wire twin of @fishaudio/agent-protocol `SessionOverrides` (session.ts).\n\nEvery field must be allow-listed in Agent.overrides_allowed; unauthorized\nfields fail session creation loudly rather than being silently dropped.", + "properties": { + "first_message": { + "anyOf": [ + { + "maxLength": 10000, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "First Message" + }, + "first_message_prompt": { + "anyOf": [ + { + "maxLength": 10000, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "First Message Prompt" + }, + "system_prompt": { + "anyOf": [ + { + "maxLength": 500000, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Limited to 32000 tokens (o200k_base), same budget as the configured prompt it replaces.", + "title": "System Prompt" + }, + "voice_id": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Voice Id" + }, + "language": { + "anyOf": [ + { + "enum": [ + "en", + "ja", + "zh", + "ko", + "es", + "fr", + "de", + "pt", + "it", + "nl", + "ar", + "hi", + "id", + "tr", + "ru", + "bg", + "sr", + "hr", + "cs", + "sk", + "pl", + "uk", + "ro", + "hu", + "el", + "sv", + "da", + "no", + "fi", + "et", + "lv", + "lt", + "ca", + "he", + "fa", + "ur", + "kk", + "ka", + "hy", + "bn", + "ta", + "te", + "kn", + "mr", + "gu", + "pa", + "ne", + "th", + "vi", + "ms", + "tl", + "af" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Language" } }, - "tags": [ - "Model" - ] + "title": "AgentSessionOverridesPayload", + "type": "object" }, - "patch": { - "summary": "Update Model", - "security": [ - { - "BearerAuth": [] + "AgentSessionAnalysisCriterionResult": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "result": { + "enum": [ + "success", + "failure", + "unknown" + ], + "title": "Result", + "type": "string" + }, + "rationale": { + "default": "", + "title": "Rationale", + "type": "string" } + }, + "required": [ + "name", + "result" ], - "parameters": [ - { - "in": "path", - "name": "id", - "description": "", - "required": true, - "schema": { - "title": "Id", - "type": "string" - }, - "deprecated": false + "title": "AgentSessionAnalysisCriterionResult", + "type": "object" + }, + "AgentSessionAnalysisDataValue": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "enum": [ + "boolean", + "text", + "number", + "enum" + ], + "title": "Type", + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Value" + }, + "rationale": { + "default": "", + "title": "Rationale", + "type": "string" } + }, + "required": [ + "name", + "type" ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "title": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Title" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "cover_image": { - "anyOf": [ - { - "format": "binary", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Cover Image" - }, - "visibility": { - "anyOf": [ - { - "enum": [ - "public", - "unlist", - "private" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Visibility" - }, - "tags": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "title": "Tags" - } - }, - "type": "object" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "properties": { - "title": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Title" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "cover_image": { - "anyOf": [ - { - "format": "binary", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Cover Image" - }, - "visibility": { - "anyOf": [ - { - "enum": [ - "public", - "unlist", - "private" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Visibility" - }, - "tags": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "title": "Tags" - } - }, - "type": "object" + "title": "AgentSessionAnalysisDataValue", + "type": "object" + }, + "AgentSessionAnalysisResult": { + "description": "Public trim of the post-call analysis record: outcomes only, no run\ninternals (model, tokens, config hash).", + "properties": { + "status": { + "enum": [ + "queued", + "running", + "completed", + "skipped", + "error" + ], + "title": "Status", + "type": "string" + }, + "summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" } + ], + "default": null, + "title": "Summary" + }, + "data": { + "items": { + "$ref": "#/components/schemas/AgentSessionAnalysisDataValue" }, - "multipart/form-data": { - "schema": { - "properties": { - "title": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Title" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "cover_image": { - "anyOf": [ - { - "format": "binary", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Cover Image" - }, - "visibility": { - "anyOf": [ - { - "enum": [ - "public", - "unlist", - "private" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Visibility" - }, - "tags": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "title": "Tags" - } - }, - "type": "object" - } + "title": "Data", + "type": "array" + }, + "criteria_results": { + "items": { + "$ref": "#/components/schemas/AgentSessionAnalysisCriterionResult" }, - "application/msgpack": { - "schema": { - "properties": { - "title": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Title" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "cover_image": { - "anyOf": [ - { - "format": "binary", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Cover Image" - }, - "visibility": { - "anyOf": [ - { - "enum": [ - "public", - "unlist", - "private" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Visibility" - }, - "tags": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "title": "Tags" - } - }, - "type": "object" - } - } + "title": "Criteria Results", + "type": "array" } }, - "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": {} + "required": [ + "status" + ], + "title": "AgentSessionAnalysisResult", + "type": "object" + }, + "AgentSessionMessageItem": { + "description": "One transcript message; system messages never surface here.", + "properties": { + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" }, - "401": { - "description": "No permission -- see authorization schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } + "runtime_message_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Runtime Message Id" + }, + "message_generation_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Message Generation Id" + }, + "llm_usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/LLMMessageUsage" + }, + { + "type": "null" } - } + ], + "default": null }, - "503": { - "description": "The server cannot process the request due to a high load", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } + "type": { + "const": "message", + "default": "message", + "title": "Type", + "type": "string" + }, + "role": { + "enum": [ + "user", + "assistant" + ], + "title": "Role", + "type": "string" + }, + "content": { + "title": "Content", + "type": "string" + }, + "turn_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" } - } + ], + "default": null, + "title": "Turn Id" + }, + "created_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Created At" } }, - "tags": [ - "Model" - ] - }, - "delete": { - "summary": "Delete Model", - "security": [ - { - "BearerAuth": [] - } - ], - "parameters": [ - { - "in": "path", - "name": "id", - "description": "", - "required": true, - "schema": { - "title": "Id", - "type": "string" - }, - "deprecated": false - } + "required": [ + "role", + "content" ], - "responses": { - "200": { - "description": "Request fulfilled, document follows", - "headers": {} + "title": "AgentSessionMessageItem", + "type": "object" + }, + "AgentSessionToolCallItem": { + "description": "Tool invocation, mirroring the realtime `tool.started` event; `call_id`\nmatches the SDK event's callId, so live UIs and history line up.", + "properties": { + "type": { + "const": "tool_call", + "default": "tool_call", + "title": "Type", + "type": "string" }, - "401": { - "description": "No permission -- see authorization schemes", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } + "call_id": { + "title": "Call Id", + "type": "string" + }, + "tool_name": { + "title": "Tool Name", + "type": "string" + }, + "tool_source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" } - } + ], + "default": null, + "title": "Tool Source" }, - "503": { - "description": "The server cannot process the request due to a high load", - "headers": {}, - "content": { - "application/json": { - "schema": { - "properties": { - "status": { - "title": "Status", - "type": "integer" - }, - "message": { - "title": "Message", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason" - } - }, - "required": [ - "status", - "message" - ], - "type": "object" - } + "input": { + "title": "Input", + "type": "string" + }, + "turn_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" } - } - } - }, - "tags": [ - "Model" - ] - } - } - }, - "tags": [], - "components": { - "securitySchemes": { - "BearerAuth": { - "type": "http", - "scheme": "bearer" - } - }, - "schemas": { - "AgentSessionSummary": { - "description": "One list row: lifecycle facts only — the conversation timeline lives on\nthe detail endpoint (Retell-style thin list).", + ], + "default": null, + "title": "Turn Id" + }, + "created_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Created At" + } + }, + "required": [ + "call_id", + "tool_name", + "input" + ], + "title": "AgentSessionToolCallItem", + "type": "object" + }, + "AgentSessionToolResultItem": { + "description": "Tool outcome (`tool.completed`/`tool.failed` twin). A call that never\nresolved (session died mid-flight) has no result item.", "properties": { - "session_id": { - "title": "Session Id", + "type": { + "const": "tool_result", + "default": "tool_result", + "title": "Type", "type": "string" }, - "agent_id": { - "title": "Agent Id", + "call_id": { + "title": "Call Id", "type": "string" }, - "agent_name": { - "title": "Agent Name", + "tool_name": { + "title": "Tool Name", "type": "string" }, - "name": { + "tool_source": { "anyOf": [ { "type": "string" @@ -14413,35 +18779,19 @@ } ], "default": null, - "title": "Name" + "title": "Tool Source" }, "status": { "enum": [ - "pending", - "active", "completed", - "failed", - "unknown" + "failed" ], "title": "Status", "type": "string" }, - "end_reason": { + "output": { "anyOf": [ { - "enum": [ - "user_hangup", - "agent_hangup", - "escalated", - "conversation_timeout", - "heartbeat_timeout", - "agent_join_timeout", - "internal_error", - "llm_endpoint_failure", - "room_join_timeout", - "cancelled", - "dial_failed" - ], "type": "string" }, { @@ -14449,36 +18799,16 @@ } ], "default": null, - "title": "End Reason" - }, - "source": { - "enum": [ - "api_key", - "dashboard", - "public", - "phone" - ], - "title": "Source", - "type": "string" + "title": "Output" }, - "direction": { - "default": "inbound", - "enum": [ - "inbound", - "outbound" - ], - "title": "Direction", - "type": "string" + "output_truncated": { + "default": false, + "title": "Output Truncated", + "type": "boolean" }, - "dial_status": { + "error": { "anyOf": [ { - "enum": [ - "answered", - "busy", - "no_answer", - "failed" - ], "type": "string" }, { @@ -14486,38 +18816,33 @@ } ], "default": null, - "title": "Dial Status" + "title": "Error" }, - "answered_by": { + "latency_ms": { "anyOf": [ { - "enum": [ - "human", - "voicemail", - "unknown" - ], - "type": "string" + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Answered By" + "title": "Latency Ms" }, - "caller_number": { + "turn_id": { "anyOf": [ { - "type": "string" + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Caller Number" + "title": "Turn Id" }, - "dialed_number": { + "created_at": { "anyOf": [ { "type": "string" @@ -14527,9 +18852,20 @@ } ], "default": null, - "title": "Dialed Number" - }, - "sip_call_id": { + "title": "Created At" + } + }, + "required": [ + "call_id", + "tool_name", + "status" + ], + "title": "AgentSessionToolResultItem", + "type": "object" + }, + "LLMMessageTokenCost": { + "properties": { + "model": { "anyOf": [ { "type": "string" @@ -14538,10 +18874,9 @@ "type": "null" } ], - "default": null, - "title": "Sip Call Id" + "title": "Model" }, - "timezone": { + "rate_version": { "anyOf": [ { "type": "string" @@ -14550,14 +18885,31 @@ "type": "null" } ], - "default": null, - "title": "Timezone" + "title": "Rate Version" }, - "created_at": { - "title": "Created At", + "category": { + "enum": [ + "input", + "cache_read", + "cache_write_5m", + "cache_write_1h", + "output" + ], + "title": "Category", "type": "string" }, - "started_at": { + "tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Tokens" + }, + "rate_per_million": { "anyOf": [ { "type": "string" @@ -14566,10 +18918,34 @@ "type": "null" } ], - "default": null, - "title": "Started At" + "title": "Rate Per Million" }, - "ended_at": { + "cost_usd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cost Usd" + } + }, + "required": [ + "model", + "rate_version", + "category", + "tokens", + "rate_per_million", + "cost_usd" + ], + "title": "LLMMessageTokenCost", + "type": "object" + }, + "LLMMessageUsage": { + "properties": { + "message_generation_id": { "anyOf": [ { "type": "string" @@ -14579,9 +18955,9 @@ } ], "default": null, - "title": "Ended At" + "title": "Message Generation Id" }, - "duration_seconds": { + "turn_id": { "anyOf": [ { "type": "integer" @@ -14591,52 +18967,111 @@ } ], "default": null, - "title": "Duration Seconds" + "title": "Turn Id" }, - "metadata": { - "additionalProperties": { - "$ref": "#/components/schemas/JsonValue" + "status": { + "enum": [ + "complete", + "pending", + "unavailable" + ], + "title": "Status", + "type": "string" + }, + "billing_mode": { + "enum": [ + "tokens", + "included", + "legacy" + ], + "title": "Billing Mode", + "type": "string" + }, + "call_count": { + "default": 0, + "title": "Call Count", + "type": "integer" + }, + "usage_complete": { + "default": false, + "title": "Usage Complete", + "type": "boolean" + }, + "call_ids": { + "items": { + "type": "string" + }, + "title": "Call Ids", + "type": "array" + }, + "cost_usd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cost Usd" + }, + "billable_cost_usd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Billable Cost Usd" + }, + "known_cost_usd": { + "default": "0", + "title": "Known Cost Usd", + "type": "string" + }, + "tokens": { + "items": { + "$ref": "#/components/schemas/LLMMessageTokenCost" }, - "title": "Metadata", - "type": "object" + "title": "Tokens", + "type": "array" } }, "required": [ - "session_id", - "agent_id", - "agent_name", "status", - "source", - "created_at" + "billing_mode" ], - "title": "AgentSessionSummary", + "title": "LLMMessageUsage", "type": "object" }, - "JsonValue": {}, - "AgentSessionCreatePayload": { - "additionalProperties": false, + "AgentSessionRecordingTrackEntity": { "properties": { - "agent_id": { - "title": "Agent Id", + "role": { + "enum": [ + "agent", + "user", + "human" + ], + "title": "Role", "type": "string" }, - "name": { - "anyOf": [ - { - "maxLength": 128, - "type": "string" - }, - { - "type": "null" - } + "status": { + "enum": [ + "requested", + "active", + "complete", + "failed" ], - "default": null, - "title": "Name" + "title": "Status", + "type": "string" }, - "timezone": { + "url": { "anyOf": [ { - "maxLength": 64, "type": "string" }, { @@ -14644,13 +19079,12 @@ } ], "default": null, - "description": "IANA timezone (like `Asia/Tokyo`) for the agent's sense of local date and time in this session. Overrides the agent's configured timezone, which defaults to UTC. Invalid names are rejected with 422.", - "title": "Timezone" + "title": "Url" }, - "client_timezone": { + "expires_at": { "anyOf": [ { - "maxLength": 64, + "format": "date-time", "type": "string" }, { @@ -14658,58 +19092,37 @@ } ], "default": null, - "description": "The end user's device timezone as an IANA name (like `Asia/Tokyo`), used as a fallback hint: it applies only when neither `timezone` nor the agent's configured timezone is set, and invalid values are ignored rather than rejected. The Web SDK fills it automatically from the browser for public agents. When your backend creates the session, forward the value from your client.", - "title": "Client Timezone" - }, - "world_context": { - "default": true, - "title": "World Context", - "type": "boolean" + "title": "Expires At" }, - "overrides": { + "duration_ms": { "anyOf": [ { - "$ref": "#/components/schemas/AgentSessionOverridesPayload" + "type": "integer" }, { "type": "null" } ], - "default": null + "default": null, + "title": "Duration Ms" }, - "dynamic_variables": { + "started_at": { "anyOf": [ { - "additionalProperties": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "integer" - }, - { - "type": "number" - }, - { - "type": "boolean" - } - ] - }, - "maxProperties": 50, - "type": "object" + "format": "date-time", + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Dynamic Variables" + "title": "Started At" }, - "end_user_id": { + "ended_at": { "anyOf": [ { - "maxLength": 256, + "format": "date-time", "type": "string" }, { @@ -14717,83 +19130,190 @@ } ], "default": null, - "title": "End User Id" + "title": "Ended At" }, - "metadata": { + "error": { "anyOf": [ { - "additionalProperties": { - "$ref": "#/components/schemas/JsonValue" - }, - "type": "object" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Metadata" + "title": "Error" }, - "llm_extra_body": { + "error_code": { "anyOf": [ { - "additionalProperties": { - "$ref": "#/components/schemas/JsonValue" - }, - "type": "object" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Llm Extra Body" + "title": "Error Code" + } + }, + "required": [ + "role", + "status" + ], + "title": "AgentSessionRecordingTrackEntity", + "type": "object" + }, + "PublicAgentEntity": { + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" }, - "tool_events": { + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "default": "", + "title": "Description", + "type": "string" + }, + "status": { + "enum": [ + "active", + "archived" + ], + "title": "Status", + "type": "string" + }, + "publication_state": { + "description": "`live` once at least one version is published; `draft` agents cannot take sessions yet.", + "enum": [ + "live", + "draft" + ], + "title": "Publication State", + "type": "string" + }, + "public_enabled": { + "default": false, + "title": "Public Enabled", + "type": "boolean" + }, + "allowed_origins": { + "items": { + "type": "string" + }, + "title": "Allowed Origins", + "type": "array" + }, + "overrides_allowed": { + "items": { + "enum": [ + "first_message", + "system_prompt", + "voice_id", + "language" + ], + "type": "string" + }, + "title": "Overrides Allowed", + "type": "array" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + } + }, + "required": [ + "agent_id", + "workspace_id", + "name", + "status", + "publication_state", + "created_at", + "updated_at" + ], + "title": "PublicAgentEntity", + "type": "object" + }, + "AgentOutboundPatch": { + "additionalProperties": false, + "properties": { + "amd_enabled": { "default": true, - "title": "Tool Events", + "title": "Amd Enabled", "type": "boolean" }, - "record_audio": { + "voicemail": { "anyOf": [ { - "type": "boolean" + "$ref": "#/components/schemas/AgentOutboundVoicemailPatch" }, { "type": "null" } ], - "default": null, - "title": "Record Audio" + "default": null } }, - "required": [ - "agent_id" - ], - "title": "AgentSessionCreatePayload", + "title": "AgentOutboundPatch", "type": "object" }, - "AgentSessionOverridesPayload": { + "AgentOutboundVoicemailPatch": { + "additionalProperties": false, + "properties": { + "action": { + "default": "hangup", + "enum": [ + "hangup", + "leave_message", + "leave_prompt_message" + ], + "title": "Action", + "type": "string" + }, + "message": { + "default": "", + "maxLength": 10000, + "title": "Message", + "type": "string" + } + }, + "title": "AgentOutboundVoicemailPatch", + "type": "object" + }, + "AgentTransferDestinationPatch": { "additionalProperties": false, - "description": "Wire twin of @fishaudio/agent-protocol `SessionOverrides` (session.ts).\n\nEvery field must be allow-listed in Agent.overrides_allowed; unauthorized\nfields fail session creation loudly rather than being silently dropped.", "properties": { - "first_message": { - "anyOf": [ - { - "maxLength": 10000, - "type": "string" - }, - { - "type": "null" - } + "type": { + "default": "phone", + "enum": [ + "phone", + "sip" ], - "default": null, - "title": "First Message" + "title": "Type", + "type": "string" }, - "first_message_prompt": { + "label": { + "default": "", + "title": "Label", + "type": "string" + }, + "phone_number": { "anyOf": [ { - "maxLength": 10000, "type": "string" }, { @@ -14801,12 +19321,11 @@ } ], "default": null, - "title": "First Message Prompt" + "title": "Phone Number" }, - "system_prompt": { + "sip_uri": { "anyOf": [ { - "maxLength": 500000, "type": "string" }, { @@ -14814,127 +19333,96 @@ } ], "default": null, - "description": "Limited to 32000 tokens (o200k_base), same budget as the configured prompt it replaces.", - "title": "System Prompt" + "title": "Sip Uri" }, - "voice_id": { - "anyOf": [ - { - "maxLength": 256, - "type": "string" - }, - { - "type": "null" - } + "description": { + "default": "", + "title": "Description", + "type": "string" + }, + "mode": { + "default": "cold", + "enum": [ + "cold", + "warm" ], - "default": null, - "title": "Voice Id" + "title": "Mode", + "type": "string" }, - "language": { - "anyOf": [ - { - "enum": [ - "en", - "ja", - "zh", - "ko", - "es", - "fr", - "de", - "pt", - "it", - "nl", - "ar", - "hi", - "id", - "tr", - "ru", - "bg", - "sr", - "hr", - "cs", - "sk", - "pl", - "uk", - "ro", - "hu", - "el", - "sv", - "da", - "no", - "fi", - "et", - "lv", - "lt", - "ca", - "he", - "fa", - "ur", - "kk", - "ka", - "hy", - "bn", - "ta", - "te", - "kn", - "mr", - "gu", - "pa", - "ne", - "th", - "vi", - "ms", - "tl", - "af" - ], - "type": "string" - }, - { - "type": "null" - } + "warm_connect": { + "default": "confirm", + "enum": [ + "confirm", + "briefing", + "direct" ], - "default": null, - "title": "Language" + "title": "Warm Connect", + "type": "string" + }, + "warm_briefing_instructions": { + "default": "", + "title": "Warm Briefing Instructions", + "type": "string" + }, + "on_failure": { + "$ref": "#/components/schemas/AgentTransferOnFailurePatch" } }, - "title": "AgentSessionOverridesPayload", + "title": "AgentTransferDestinationPatch", "type": "object" }, - "AgentSessionAnalysisCriterionResult": { + "AgentTransferOnFailurePatch": { + "additionalProperties": false, "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "result": { + "action": { + "default": "return_to_agent", "enum": [ - "success", - "failure", - "unknown" + "return_to_agent", + "end_call" ], - "title": "Result", + "title": "Action", "type": "string" }, - "rationale": { + "message": { "default": "", - "title": "Rationale", + "title": "Message", + "type": "string" + } + }, + "title": "AgentTransferOnFailurePatch", + "type": "object" + }, + "PublicAgentAnalysisCriterion": { + "additionalProperties": false, + "properties": { + "name": { + "pattern": "^[a-z][a-z0-9_]{0,63}$", + "title": "Name", + "type": "string" + }, + "description": { + "default": "", + "maxLength": 500, + "title": "Description", "type": "string" } }, "required": [ - "name", - "result" + "name" ], - "title": "AgentSessionAnalysisCriterionResult", + "title": "PublicAgentAnalysisCriterion", "type": "object" }, - "AgentSessionAnalysisDataValue": { + "PublicAgentAnalysisDataField": { + "additionalProperties": false, "properties": { "name": { + "pattern": "^[a-z][a-z0-9_]{0,63}$", "title": "Name", "type": "string" }, "type": { + "default": "text", "enum": [ "boolean", "text", @@ -14944,204 +19432,179 @@ "title": "Type", "type": "string" }, - "value": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Value" - }, - "rationale": { + "description": { "default": "", - "title": "Rationale", - "type": "string" - } - }, - "required": [ - "name", - "type" - ], - "title": "AgentSessionAnalysisDataValue", - "type": "object" - }, - "AgentSessionAnalysisResult": { - "description": "Public trim of the post-call analysis record: outcomes only, no run\ninternals (model, tokens, config hash).", - "properties": { - "status": { - "enum": [ - "queued", - "running", - "completed", - "skipped", - "error" - ], - "title": "Status", + "maxLength": 500, + "title": "Description", "type": "string" }, - "summary": { + "enum_options": { "anyOf": [ { - "type": "string" + "items": { + "type": "string" + }, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Summary" - }, - "data": { - "items": { - "$ref": "#/components/schemas/AgentSessionAnalysisDataValue" - }, - "title": "Data", - "type": "array" - }, - "criteria_results": { - "items": { - "$ref": "#/components/schemas/AgentSessionAnalysisCriterionResult" - }, - "title": "Criteria Results", - "type": "array" + "title": "Enum Options" } }, "required": [ - "status" + "name" ], - "title": "AgentSessionAnalysisResult", + "title": "PublicAgentAnalysisDataField", "type": "object" }, - "AgentSessionMessageItem": { - "description": "One transcript message; system messages never surface here.", + "PublicAgentAnalysisPatch": { + "additionalProperties": false, "properties": { - "id": { + "summary": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentAnalysisSummaryPatch" }, { "type": "null" } ], - "default": null, - "title": "Id" + "default": null }, - "runtime_message_id": { + "data_fields": { "anyOf": [ { - "type": "string" + "items": { + "$ref": "#/components/schemas/PublicAgentAnalysisDataField" + }, + "maxItems": 20, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Runtime Message Id" + "title": "Data Fields" }, - "message_generation_id": { + "criteria": { "anyOf": [ { - "type": "string" + "items": { + "$ref": "#/components/schemas/PublicAgentAnalysisCriterion" + }, + "maxItems": 10, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Message Generation Id" + "title": "Criteria" + } + }, + "title": "PublicAgentAnalysisPatch", + "type": "object" + }, + "PublicAgentAnalysisSummaryPatch": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": true, + "title": "Enabled", + "type": "boolean" }, - "llm_usage": { + "prompt": { "anyOf": [ { - "$ref": "#/components/schemas/LLMMessageUsage" + "maxLength": 1000, + "type": "string" }, { "type": "null" } ], - "default": null - }, - "type": { - "const": "message", - "default": "message", - "title": "Type", - "type": "string" + "default": null, + "title": "Prompt" }, - "role": { + "language": { + "default": "en", "enum": [ - "user", - "assistant" + "en", + "ja", + "zh", + "ko", + "es", + "fr", + "de", + "pt", + "it", + "nl", + "ar", + "hi", + "id", + "tr", + "ru", + "bg", + "sr", + "hr", + "cs", + "sk", + "pl", + "uk", + "ro", + "hu", + "el", + "sv", + "da", + "no", + "fi", + "et", + "lv", + "lt", + "ca", + "he", + "fa", + "ur", + "kk", + "ka", + "hy", + "bn", + "ta", + "te", + "kn", + "mr", + "gu", + "pa", + "ne", + "th", + "vi", + "ms", + "tl", + "af" ], - "title": "Role", - "type": "string" - }, - "content": { - "title": "Content", + "title": "Language", "type": "string" - }, - "turn_id": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Turn Id" - }, - "created_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Created At" } }, - "required": [ - "role", - "content" - ], - "title": "AgentSessionMessageItem", + "title": "PublicAgentAnalysisSummaryPatch", "type": "object" }, - "AgentSessionToolCallItem": { - "description": "Tool invocation, mirroring the realtime `tool.started` event; `call_id`\nmatches the SDK event's callId, so live UIs and history line up.", + "PublicAgentAsrPatch": { + "additionalProperties": false, "properties": { - "type": { - "const": "tool_call", - "default": "tool_call", - "title": "Type", - "type": "string" - }, - "call_id": { - "title": "Call Id", - "type": "string" - }, - "tool_name": { - "title": "Tool Name", - "type": "string" - }, - "tool_source": { + "model": { "anyOf": [ { + "enum": [ + "deepgram:nova-3", + "elevenlabs:scribe_v2_realtime", + "elevenlabs:scribe_v2_medical" + ], "type": "string" }, { @@ -15149,253 +19612,192 @@ } ], "default": null, - "title": "Tool Source" - }, - "input": { - "title": "Input", - "type": "string" + "title": "Model" }, - "turn_id": { + "multilingual": { "anyOf": [ { - "type": "integer" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Turn Id" + "title": "Multilingual" }, - "created_at": { + "strict_language": { "anyOf": [ { - "type": "string" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Created At" - } - }, - "required": [ - "call_id", - "tool_name", - "input" - ], - "title": "AgentSessionToolCallItem", - "type": "object" - }, - "AgentSessionToolResultItem": { - "description": "Tool outcome (`tool.completed`/`tool.failed` twin). A call that never\nresolved (session died mid-flight) has no result item.", - "properties": { - "type": { - "const": "tool_result", - "default": "tool_result", - "title": "Type", - "type": "string" - }, - "call_id": { - "title": "Call Id", - "type": "string" - }, - "tool_name": { - "title": "Tool Name", - "type": "string" + "description": "Enforces voice.speaking_language whatever multilingual says: speech recognized as another language reaches the agent as [unintelligible speech]. Language is detected per utterance, so a short or heavily accented phrase in the speaking language can occasionally be detected as another language and replaced too. With deepgram:nova-3, speech in another language is usually not transcribed at all. Only enable it when you explicitly need to stop the agent from understanding other languages.", + "title": "Strict Language" }, - "tool_source": { + "keyterms": { "anyOf": [ { - "type": "string" + "items": { + "type": "string" + }, + "maxItems": 50, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Tool Source" - }, - "status": { - "enum": [ - "completed", - "failed" - ], - "title": "Status", - "type": "string" - }, - "output": { + "title": "Keyterms" + } + }, + "title": "PublicAgentAsrPatch", + "type": "object" + }, + "PublicAgentConfigPatchPayload": { + "additionalProperties": false, + "properties": { + "prompt": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentPromptPatch" }, { "type": "null" } ], - "default": null, - "title": "Output" - }, - "output_truncated": { - "default": false, - "title": "Output Truncated", - "type": "boolean" + "default": null }, - "error": { + "voice": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentVoicePatch" }, { "type": "null" } ], - "default": null, - "title": "Error" + "default": null }, - "latency_ms": { + "asr": { "anyOf": [ { - "type": "integer" + "$ref": "#/components/schemas/PublicAgentAsrPatch" }, { "type": "null" } ], - "default": null, - "title": "Latency Ms" + "default": null }, - "turn_id": { + "conversation": { "anyOf": [ { - "type": "integer" + "$ref": "#/components/schemas/PublicAgentConversationPatch" }, { "type": "null" } ], - "default": null, - "title": "Turn Id" + "default": null }, - "created_at": { + "tools": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentToolsPatch" }, { "type": "null" } ], - "default": null, - "title": "Created At" - } - }, - "required": [ - "call_id", - "tool_name", - "status" - ], - "title": "AgentSessionToolResultItem", - "type": "object" - }, - "LLMMessageTokenCost": { - "properties": { - "model": { + "default": null + }, + "webhooks": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentWebhooksPatch" }, { "type": "null" } ], - "title": "Model" + "default": null }, - "rate_version": { + "knowledge_base": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentKnowledgeBasePatch" }, { "type": "null" } ], - "title": "Rate Version" - }, - "category": { - "enum": [ - "input", - "cache_read", - "cache_write_5m", - "cache_write_1h", - "output" - ], - "title": "Category", - "type": "string" + "default": null }, - "tokens": { + "analysis": { "anyOf": [ { - "type": "integer" + "$ref": "#/components/schemas/PublicAgentAnalysisPatch" }, { "type": "null" } ], - "title": "Tokens" + "default": null }, - "rate_per_million": { + "guardrails": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentGuardrailsPatch" }, { "type": "null" } ], - "title": "Rate Per Million" + "default": null }, - "cost_usd": { + "llm": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/PublicAgentLLMPatch" }, { "type": "null" } ], - "title": "Cost Usd" + "default": null } }, - "required": [ - "model", - "rate_version", - "category", - "tokens", - "rate_per_million", - "cost_usd" - ], - "title": "LLMMessageTokenCost", + "title": "PublicAgentConfigPatchPayload", "type": "object" }, - "LLMMessageUsage": { + "PublicAgentConversationPatch": { + "additionalProperties": false, "properties": { - "message_generation_id": { + "max_duration_seconds": { "anyOf": [ { - "type": "string" + "maximum": 3600, + "minimum": 60, + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Message Generation Id" + "title": "Max Duration Seconds" }, - "turn_id": { + "response_wait_ms": { "anyOf": [ { + "maximum": 2000, + "minimum": 300, "type": "integer" }, { @@ -15403,46 +19805,16 @@ } ], "default": null, - "title": "Turn Id" - }, - "status": { - "enum": [ - "complete", - "pending", - "unavailable" - ], - "title": "Status", - "type": "string" - }, - "billing_mode": { - "enum": [ - "tokens", - "included", - "legacy" - ], - "title": "Billing Mode", - "type": "string" - }, - "call_count": { - "default": 0, - "title": "Call Count", - "type": "integer" - }, - "usage_complete": { - "default": false, - "title": "Usage Complete", - "type": "boolean" - }, - "call_ids": { - "items": { - "type": "string" - }, - "title": "Call Ids", - "type": "array" + "title": "Response Wait Ms" }, - "cost_usd": { + "eagerness": { "anyOf": [ { + "enum": [ + "relaxed", + "balanced", + "eager" + ], "type": "string" }, { @@ -15450,77 +19822,42 @@ } ], "default": null, - "title": "Cost Usd" + "title": "Eagerness" }, - "billable_cost_usd": { + "response_max_wait_ms": { "anyOf": [ { - "type": "string" + "maximum": 4000, + "minimum": 0, + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Billable Cost Usd" - }, - "known_cost_usd": { - "default": "0", - "title": "Known Cost Usd", - "type": "string" - }, - "tokens": { - "items": { - "$ref": "#/components/schemas/LLMMessageTokenCost" - }, - "title": "Tokens", - "type": "array" - } - }, - "required": [ - "status", - "billing_mode" - ], - "title": "LLMMessageUsage", - "type": "object" - }, - "AgentSessionRecordingTrackEntity": { - "properties": { - "role": { - "enum": [ - "agent", - "user", - "human" - ], - "title": "Role", - "type": "string" - }, - "status": { - "enum": [ - "requested", - "active", - "complete", - "failed" - ], - "title": "Status", - "type": "string" + "title": "Response Max Wait Ms" }, - "url": { + "interruptible": { "anyOf": [ { - "type": "string" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Url" + "title": "Interruptible" }, - "expires_at": { + "interruption_sensitivity": { "anyOf": [ { - "format": "date-time", + "enum": [ + "low", + "balanced", + "high" + ], "type": "string" }, { @@ -15528,228 +19865,90 @@ } ], "default": null, - "title": "Expires At" + "title": "Interruption Sensitivity" }, - "duration_ms": { + "interruption_ignore_phrases": { "anyOf": [ { - "type": "integer" + "items": { + "type": "string" + }, + "maxItems": 50, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Duration Ms" + "title": "Interruption Ignore Phrases" }, - "started_at": { + "speculative_response": { "anyOf": [ { - "format": "date-time", - "type": "string" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Started At" + "title": "Speculative Response" }, - "ended_at": { + "reengage_enabled": { "anyOf": [ { - "format": "date-time", - "type": "string" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Ended At" + "title": "Reengage Enabled" }, - "error": { + "hangup_after_seconds": { "anyOf": [ { - "type": "string" + "maximum": 3600, + "minimum": 10, + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Error" + "title": "Hangup After Seconds" }, - "error_code": { + "hangup_farewell_enabled": { "anyOf": [ { - "type": "string" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Error Code" - } - }, - "required": [ - "role", - "status" - ], - "title": "AgentSessionRecordingTrackEntity", - "type": "object" - }, - "PublicAgentEntity": { - "properties": { - "agent_id": { - "title": "Agent Id", - "type": "string" - }, - "workspace_id": { - "title": "Workspace Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "status": { - "enum": [ - "active", - "archived" - ], - "title": "Status", - "type": "string" - }, - "publication_state": { - "description": "`live` once at least one version is published; `draft` agents cannot take sessions yet.", - "enum": [ - "live", - "draft" - ], - "title": "Publication State", - "type": "string" - }, - "public_enabled": { - "default": false, - "title": "Public Enabled", - "type": "boolean" - }, - "allowed_origins": { - "items": { - "type": "string" - }, - "title": "Allowed Origins", - "type": "array" - }, - "overrides_allowed": { - "items": { - "enum": [ - "first_message", - "system_prompt", - "voice_id", - "language" - ], - "type": "string" - }, - "title": "Overrides Allowed", - "type": "array" - }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" - } - }, - "required": [ - "agent_id", - "workspace_id", - "name", - "status", - "publication_state", - "created_at", - "updated_at" - ], - "title": "PublicAgentEntity", - "type": "object" - }, - "AgentOutboundPatch": { - "additionalProperties": false, - "properties": { - "amd_enabled": { - "default": true, - "title": "Amd Enabled", - "type": "boolean" + "title": "Hangup Farewell Enabled" }, - "voicemail": { + "record_audio": { "anyOf": [ { - "$ref": "#/components/schemas/AgentOutboundVoicemailPatch" + "type": "boolean" }, { "type": "null" } ], - "default": null - } - }, - "title": "AgentOutboundPatch", - "type": "object" - }, - "AgentOutboundVoicemailPatch": { - "additionalProperties": false, - "properties": { - "action": { - "default": "hangup", - "enum": [ - "hangup", - "leave_message", - "leave_prompt_message" - ], - "title": "Action", - "type": "string" - }, - "message": { - "default": "", - "maxLength": 10000, - "title": "Message", - "type": "string" - } - }, - "title": "AgentOutboundVoicemailPatch", - "type": "object" - }, - "AgentTransferDestinationPatch": { - "additionalProperties": false, - "properties": { - "type": { - "default": "phone", - "enum": [ - "phone", - "sip" - ], - "title": "Type", - "type": "string" - }, - "label": { - "default": "", - "title": "Label", - "type": "string" + "default": null, + "title": "Record Audio" }, - "phone_number": { + "timezone": { "anyOf": [ { + "maxLength": 64, "type": "string" }, { @@ -15757,182 +19956,125 @@ } ], "default": null, - "title": "Phone Number" + "title": "Timezone" }, - "sip_uri": { + "transfer_destinations": { "anyOf": [ { - "type": "string" + "items": { + "$ref": "#/components/schemas/AgentTransferDestinationPatch" + }, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Sip Uri" - }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "mode": { - "default": "cold", - "enum": [ - "cold", - "warm" - ], - "title": "Mode", - "type": "string" - }, - "warm_connect": { - "default": "confirm", - "enum": [ - "confirm", - "briefing", - "direct" - ], - "title": "Warm Connect", - "type": "string" - }, - "warm_briefing_instructions": { - "default": "", - "title": "Warm Briefing Instructions", - "type": "string" + "title": "Transfer Destinations" }, - "on_failure": { - "$ref": "#/components/schemas/AgentTransferOnFailurePatch" - } - }, - "title": "AgentTransferDestinationPatch", - "type": "object" - }, - "AgentTransferOnFailurePatch": { - "additionalProperties": false, - "properties": { - "action": { - "default": "return_to_agent", - "enum": [ - "return_to_agent", - "end_call" + "outbound": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentOutboundPatch" + }, + { + "type": "null" + } ], - "title": "Action", - "type": "string" - }, - "message": { - "default": "", - "title": "Message", - "type": "string" - } - }, - "title": "AgentTransferOnFailurePatch", - "type": "object" - }, - "PublicAgentAnalysisCriterion": { - "additionalProperties": false, - "properties": { - "name": { - "pattern": "^[a-z][a-z0-9_]{0,63}$", - "title": "Name", - "type": "string" - }, - "description": { - "default": "", - "maxLength": 500, - "title": "Description", - "type": "string" + "default": null } - }, - "required": [ - "name" - ], - "title": "PublicAgentAnalysisCriterion", + }, + "title": "PublicAgentConversationPatch", "type": "object" }, - "PublicAgentAnalysisDataField": { + "PublicAgentCreatePayload": { "additionalProperties": false, "properties": { "name": { - "pattern": "^[a-z][a-z0-9_]{0,63}$", + "maxLength": 120, + "minLength": 1, "title": "Name", "type": "string" }, - "type": { - "default": "text", - "enum": [ - "boolean", - "text", - "number", - "enum" - ], - "title": "Type", - "type": "string" - }, "description": { "default": "", - "maxLength": 500, + "maxLength": 2000, "title": "Description", "type": "string" }, - "enum_options": { + "config": { "anyOf": [ { - "items": { - "type": "string" - }, - "type": "array" + "$ref": "#/components/schemas/PublicAgentConfigPatchPayload" }, { "type": "null" } ], "default": null, - "title": "Enum Options" + "description": "Optional initial draft configuration, applied atop the platform defaults; one call provisions a fully configured agent." } }, "required": [ "name" ], - "title": "PublicAgentAnalysisDataField", + "title": "PublicAgentCreatePayload", "type": "object" }, - "PublicAgentAnalysisPatch": { + "PublicAgentGuardrailsPatch": { "additionalProperties": false, "properties": { - "summary": { + "focus": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentAnalysisSummaryPatch" + "type": "boolean" }, { "type": "null" } ], - "default": null + "default": null, + "title": "Focus" }, - "data_fields": { + "manipulation": { "anyOf": [ { - "items": { - "$ref": "#/components/schemas/PublicAgentAnalysisDataField" - }, - "maxItems": 20, - "type": "array" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Data Fields" + "title": "Manipulation" + } + }, + "title": "PublicAgentGuardrailsPatch", + "type": "object" + }, + "PublicAgentKnowledgeBasePatch": { + "additionalProperties": false, + "properties": { + "enabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether the agent retrieves from its attached sources. Defaults to true when omitted alongside a non-empty knowledge_source_ids; send false explicitly to attach sources without using them yet.", + "title": "Enabled" }, - "criteria": { + "knowledge_source_ids": { "anyOf": [ { "items": { - "$ref": "#/components/schemas/PublicAgentAnalysisCriterion" + "type": "string" }, - "maxItems": 10, "type": "array" }, { @@ -15940,24 +20082,33 @@ } ], "default": null, - "title": "Criteria" + "description": "Replaces the attached knowledge sources wholesale. Every id must be an existing knowledge source visible to the agent, else 422.", + "title": "Knowledge Source Ids" } }, - "title": "PublicAgentAnalysisPatch", + "title": "PublicAgentKnowledgeBasePatch", "type": "object" }, - "PublicAgentAnalysisSummaryPatch": { + "PublicAgentLLMCustomConfig": { "additionalProperties": false, "properties": { - "enabled": { - "default": true, - "title": "Enabled", - "type": "boolean" + "base_url": { + "maxLength": 2000, + "minLength": 1, + "title": "Base Url", + "type": "string" }, - "prompt": { + "model": { + "maxLength": 256, + "minLength": 1, + "title": "Model", + "type": "string" + }, + "api_key": { "anyOf": [ { - "maxLength": 1000, + "maxLength": 4096, + "minLength": 1, "type": "string" }, { @@ -15965,81 +20116,33 @@ } ], "default": null, - "title": "Prompt" - }, - "language": { - "default": "en", - "enum": [ - "en", - "ja", - "zh", - "ko", - "es", - "fr", - "de", - "pt", - "it", - "nl", - "ar", - "hi", - "id", - "tr", - "ru", - "bg", - "sr", - "hr", - "cs", - "sk", - "pl", - "uk", - "ro", - "hu", - "el", - "sv", - "da", - "no", - "fi", - "et", - "lv", - "lt", - "ca", - "he", - "fa", - "ur", - "kk", - "ka", - "hy", - "bn", - "ta", - "te", - "kn", - "mr", - "gu", - "pa", - "ne", - "th", - "vi", - "ms", - "tl", - "af" - ], - "title": "Language", - "type": "string" + "description": "Sent to your endpoint as the Authorization bearer token; write-only, reads return null. Required when introducing `custom`; omit to keep the stored key when updating.", + "title": "Api Key" } }, - "title": "PublicAgentAnalysisSummaryPatch", + "required": [ + "base_url", + "model" + ], + "title": "PublicAgentLLMCustomConfig", "type": "object" }, - "PublicAgentAsrPatch": { + "PublicAgentLLMPatch": { "additionalProperties": false, "properties": { "model": { "anyOf": [ { "enum": [ - "deepgram:nova-3", - "elevenlabs:scribe_v2_realtime", - "elevenlabs:scribe_v2_medical" + "google/gemini-3.5-flash-lite", + "google/gemini-3.6-flash", + "google/gemini-2.5-flash", + "google/gemma-4-26b-a4b-it", + "anthropic/claude-haiku-4.5", + "anthropic/claude-sonnet-4.6", + "openai/gpt-5.6-luna", + "openai/gpt-4o", + "openai/gpt-4.1" ], "type": "string" }, @@ -16050,207 +20153,388 @@ "default": null, "title": "Model" }, - "multilingual": { + "custom": { "anyOf": [ { - "type": "boolean" + "$ref": "#/components/schemas/PublicAgentLLMCustomConfig" }, { "type": "null" } ], "default": null, - "title": "Multilingual" + "description": "Your own OpenAI-compatible endpoint; mutually exclusive with `model`, explicit null reverts to the platform model." + } + }, + "title": "PublicAgentLLMPatch", + "type": "object" + }, + "PublicAgentPromptPatch": { + "additionalProperties": false, + "properties": { + "system_prompt": { + "anyOf": [ + { + "maxLength": 500000, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Limited to 32000 tokens (o200k_base); keeping it under 2000 tokens is recommended for latency and cost.", + "title": "System Prompt" }, - "strict_language": { + "first_message_mode": { "anyOf": [ { - "type": "boolean" + "enum": [ + "off", + "fixed", + "prompt" + ], + "type": "string" }, { "type": "null" } ], "default": null, - "description": "Enforces voice.speaking_language whatever multilingual says: speech recognized as another language reaches the agent as [unintelligible speech]. Language is detected per utterance, so a short or heavily accented phrase in the speaking language can occasionally be detected as another language and replaced too. With deepgram:nova-3, speech in another language is usually not transcribed at all. Only enable it when you explicitly need to stop the agent from understanding other languages.", - "title": "Strict Language" + "title": "First Message Mode" }, - "keyterms": { + "first_message": { "anyOf": [ { - "items": { - "type": "string" - }, - "maxItems": 50, - "type": "array" + "maxLength": 10000, + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Keyterms" + "title": "First Message" + }, + "first_message_prompt": { + "anyOf": [ + { + "maxLength": 10000, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "First Message Prompt" } }, - "title": "PublicAgentAsrPatch", + "title": "PublicAgentPromptPatch", "type": "object" }, - "PublicAgentConfigPatchPayload": { + "PublicAgentToolsPatch": { "additionalProperties": false, "properties": { - "prompt": { + "enabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Enabled" + }, + "tool_ids": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentPromptPatch" + "items": { + "type": "string" + }, + "type": "array" }, { "type": "null" } ], - "default": null + "default": null, + "description": "Replaces the attached tool set wholesale. Every id must be an existing tool in the agent's workspace, else 422.", + "title": "Tool Ids" }, - "voice": { + "system_tools": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentVoicePatch" + "$ref": "#/components/schemas/PublicSystemToolsPatch" }, { "type": "null" } ], "default": null - }, - "asr": { + } + }, + "title": "PublicAgentToolsPatch", + "type": "object" + }, + "PublicAgentVoicePatch": { + "additionalProperties": false, + "properties": { + "voice_id": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentAsrPatch" + "maxLength": 256, + "type": "string" }, { "type": "null" } ], - "default": null + "default": null, + "title": "Voice Id" }, - "conversation": { + "speaking_language": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentConversationPatch" + "enum": [ + "en", + "ja", + "zh", + "ko", + "es", + "fr", + "de", + "pt", + "it", + "nl", + "ar", + "hi", + "id", + "tr", + "ru", + "bg", + "sr", + "hr", + "cs", + "sk", + "pl", + "uk", + "ro", + "hu", + "el", + "sv", + "da", + "no", + "fi", + "et", + "lv", + "lt", + "ca", + "he", + "fa", + "ur", + "kk", + "ka", + "hy", + "bn", + "ta", + "te", + "kn", + "mr", + "gu", + "pa", + "ne", + "th", + "vi", + "ms", + "tl", + "af" + ], + "type": "string" }, { "type": "null" } ], - "default": null + "default": null, + "title": "Speaking Language" }, - "tools": { + "expressive": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentToolsPatch" + "type": "boolean" }, { "type": "null" } ], - "default": null + "default": null, + "title": "Expressive" }, - "webhooks": { + "speed": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentWebhooksPatch" + "maximum": 2, + "minimum": 0.5, + "type": "number" }, { "type": "null" } ], - "default": null - }, - "knowledge_base": { + "default": null, + "title": "Speed" + } + }, + "title": "PublicAgentVoicePatch", + "type": "object" + }, + "PublicAgentWebhooksPatch": { + "additionalProperties": false, + "properties": { + "conversation_init": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentKnowledgeBasePatch" + "$ref": "#/components/schemas/PublicConversationInitWebhookPayload" }, { "type": "null" } ], - "default": null + "default": null, + "description": "Pre-call webhook for inbound phone calls: called while the call is being set up; the response's dynamic_variables render into the published prompt before the agent speaks. An explicit null removes it. Takes effect after the next publish." }, - "analysis": { + "post_call": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentAnalysisPatch" + "items": { + "$ref": "#/components/schemas/PublicPostCallWebhookPayload" + }, + "maxItems": 5, + "type": "array" }, { "type": "null" } ], - "default": null + "default": null, + "description": "Up to 5 endpoints, each receiving every post-call event. Replaces the configured list wholesale; a single object is still accepted and is stored as a one-element list, and null (or an empty list) clears every endpoint. URLs must be unique.", + "title": "Post Call" + } + }, + "title": "PublicAgentWebhooksPatch", + "type": "object" + }, + "PublicConversationInitWebhookPayload": { + "additionalProperties": false, + "properties": { + "url": { + "maxLength": 4000, + "minLength": 1, + "title": "Url", + "type": "string" }, - "guardrails": { + "secret": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentGuardrailsPatch" + "maxLength": 256, + "type": "string" }, { "type": "null" } ], - "default": null + "default": null, + "description": "Optional HMAC-SHA256 signing secret (X-Fish-Webhook-Signature). Write-only: reads report `has_secret` instead of the value. Omit it (or send null) to keep the secret already stored for the same url; send an empty string to remove it.", + "title": "Secret" }, - "llm": { + "timeout_seconds": { + "default": 5, + "description": "How long the platform waits for the endpoint before connecting the call without variables.", + "maximum": 8, + "minimum": 1, + "title": "Timeout Seconds", + "type": "number" + } + }, + "required": [ + "url" + ], + "title": "PublicConversationInitWebhookPayload", + "type": "object" + }, + "PublicPostCallWebhookPayload": { + "additionalProperties": false, + "properties": { + "url": { + "maxLength": 4000, + "minLength": 1, + "title": "Url", + "type": "string" + }, + "secret": { "anyOf": [ { - "$ref": "#/components/schemas/PublicAgentLLMPatch" + "maxLength": 256, + "type": "string" }, { "type": "null" } ], - "default": null + "default": null, + "description": "Optional HMAC-SHA256 signing secret (X-Fish-Webhook-Signature). Write-only: reads report `has_secret` instead of the value. Omit it (or send null) to keep the secret already stored for the same url; send an empty string to remove it.", + "title": "Secret" } }, - "title": "PublicAgentConfigPatchPayload", + "required": [ + "url" + ], + "title": "PublicPostCallWebhookPayload", "type": "object" }, - "PublicAgentConversationPatch": { + "PublicSystemToolsPatch": { "additionalProperties": false, "properties": { - "max_duration_seconds": { + "hang_up_call": { "anyOf": [ { - "maximum": 3600, - "minimum": 60, - "type": "integer" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Max Duration Seconds" - }, - "response_wait_ms": { + "title": "Hang Up Call" + } + }, + "title": "PublicSystemToolsPatch", + "type": "object" + }, + "PublicAgentUpdatePayload": { + "additionalProperties": false, + "properties": { + "name": { "anyOf": [ { - "maximum": 2000, - "minimum": 300, - "type": "integer" + "maxLength": 120, + "minLength": 1, + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Response Wait Ms" + "title": "Name" }, - "eagerness": { + "description": { "anyOf": [ { - "enum": [ - "relaxed", - "balanced", - "eager" - ], + "maxLength": 2000, "type": "string" }, { @@ -16258,23 +20542,25 @@ } ], "default": null, - "title": "Eagerness" + "title": "Description" }, - "response_max_wait_ms": { + "status": { "anyOf": [ { - "maximum": 4000, - "minimum": 0, - "type": "integer" + "enum": [ + "active", + "archived" + ], + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Response Max Wait Ms" + "title": "Status" }, - "interruptible": { + "public_enabled": { "anyOf": [ { "type": "boolean" @@ -16284,32 +20570,38 @@ } ], "default": null, - "title": "Interruptible" + "description": "Allow keyless session creation from the browser SDK, gated by allowed_origins.", + "title": "Public Enabled" }, - "interruption_sensitivity": { + "allowed_origins": { "anyOf": [ { - "enum": [ - "low", - "balanced", - "high" - ], - "type": "string" + "items": { + "type": "string" + }, + "maxItems": 20, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Interruption Sensitivity" + "title": "Allowed Origins" }, - "interruption_ignore_phrases": { + "overrides_allowed": { "anyOf": [ { "items": { + "enum": [ + "first_message", + "system_prompt", + "voice_id", + "language" + ], "type": "string" }, - "maxItems": 50, + "maxItems": 8, "type": "array" }, { @@ -16317,37 +20609,247 @@ } ], "default": null, - "title": "Interruption Ignore Phrases" + "description": "Which session-creation overrides callers may use. An empty list rejects all overrides.", + "title": "Overrides Allowed" + } + }, + "title": "PublicAgentUpdatePayload", + "type": "object" + }, + "AgentAnalysisConfig": { + "properties": { + "summary": { + "$ref": "#/components/schemas/AgentAnalysisSummaryConfig" }, - "speculative_response": { + "data_fields": { + "items": { + "$ref": "#/components/schemas/AgentAnalysisDataField" + }, + "maxItems": 20, + "title": "Data Fields", + "type": "array" + }, + "criteria": { + "items": { + "$ref": "#/components/schemas/AgentAnalysisCriterion" + }, + "maxItems": 10, + "title": "Criteria", + "type": "array" + } + }, + "title": "AgentAnalysisConfig", + "type": "object" + }, + "AgentAnalysisCriterion": { + "properties": { + "name": { + "pattern": "^[a-z][a-z0-9_]{0,63}$", + "title": "Name", + "type": "string" + }, + "description": { + "default": "", + "maxLength": 500, + "title": "Description", + "type": "string" + } + }, + "required": [ + "name" + ], + "title": "AgentAnalysisCriterion", + "type": "object" + }, + "AgentAnalysisDataField": { + "properties": { + "name": { + "pattern": "^[a-z][a-z0-9_]{0,63}$", + "title": "Name", + "type": "string" + }, + "type": { + "default": "text", + "enum": [ + "boolean", + "text", + "number", + "enum" + ], + "title": "Type", + "type": "string" + }, + "description": { + "default": "", + "maxLength": 500, + "title": "Description", + "type": "string" + }, + "enum_options": { "anyOf": [ { - "type": "boolean" + "items": { + "type": "string" + }, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Speculative Response" + "title": "Enum Options" + } + }, + "required": [ + "name" + ], + "title": "AgentAnalysisDataField", + "type": "object" + }, + "AgentAnalysisSummaryConfig": { + "properties": { + "enabled": { + "default": true, + "title": "Enabled", + "type": "boolean" }, - "reengage_enabled": { + "prompt": { "anyOf": [ { - "type": "boolean" + "maxLength": 1000, + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Reengage Enabled" + "title": "Prompt" }, - "hangup_after_seconds": { + "language": { + "default": "en", + "enum": [ + "en", + "ja", + "zh", + "ko", + "es", + "fr", + "de", + "pt", + "it", + "nl", + "ar", + "hi", + "id", + "tr", + "ru", + "bg", + "sr", + "hr", + "cs", + "sk", + "pl", + "uk", + "ro", + "hu", + "el", + "sv", + "da", + "no", + "fi", + "et", + "lv", + "lt", + "ca", + "he", + "fa", + "ur", + "kk", + "ka", + "hy", + "bn", + "ta", + "te", + "kn", + "mr", + "gu", + "pa", + "ne", + "th", + "vi", + "ms", + "tl", + "af" + ], + "title": "Language", + "type": "string" + } + }, + "title": "AgentAnalysisSummaryConfig", + "type": "object" + }, + "AgentAsrConfig": { + "properties": { + "model": { + "default": "deepgram:nova-3", + "enum": [ + "deepgram:nova-3", + "elevenlabs:scribe_v2_realtime", + "elevenlabs:scribe_v2_medical" + ], + "title": "Model", + "type": "string" + }, + "multilingual": { + "default": false, + "title": "Multilingual", + "type": "boolean" + }, + "strict_language": { + "default": false, + "description": "Enforces voice.speaking_language whatever multilingual says: speech recognized as another language reaches the agent as [unintelligible speech]. Language is detected per utterance, so a short or heavily accented phrase in the speaking language can occasionally be detected as another language and replaced too. With deepgram:nova-3, speech in another language is usually not transcribed at all. Only enable it when you explicitly need to stop the agent from understanding other languages.", + "title": "Strict Language", + "type": "boolean" + }, + "keyterms": { + "items": { + "type": "string" + }, + "maxItems": 50, + "title": "Keyterms", + "type": "array" + } + }, + "title": "AgentAsrConfig", + "type": "object" + }, + "AgentConversationConfig": { + "properties": { + "max_duration_seconds": { + "default": 1800, + "title": "Max Duration Seconds", + "type": "integer" + }, + "response_wait_ms": { + "default": 550, + "title": "Response Wait Ms", + "type": "integer" + }, + "eagerness": { + "default": "balanced", + "enum": [ + "relaxed", + "balanced", + "eager" + ], + "title": "Eagerness", + "type": "string" + }, + "response_max_wait_ms": { "anyOf": [ { - "maximum": 3600, - "minimum": 10, "type": "integer" }, { @@ -16355,64 +20857,171 @@ } ], "default": null, - "title": "Hangup After Seconds" + "title": "Response Max Wait Ms" }, - "hangup_farewell_enabled": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } + "interruptible": { + "default": true, + "title": "Interruptible", + "type": "boolean" + }, + "interruption_sensitivity": { + "default": "balanced", + "enum": [ + "low", + "balanced", + "high" ], - "default": null, - "title": "Hangup Farewell Enabled" + "title": "Interruption Sensitivity", + "type": "string" + }, + "interruption_ignore_phrases": { + "default": [], + "items": { + "type": "string" + }, + "title": "Interruption Ignore Phrases", + "type": "array" + }, + "speculative_response": { + "default": true, + "title": "Speculative Response", + "type": "boolean" + }, + "reengage_enabled": { + "default": false, + "title": "Reengage Enabled", + "type": "boolean" + }, + "hangup_after_seconds": { + "default": 60, + "title": "Hangup After Seconds", + "type": "integer" + }, + "hangup_farewell_enabled": { + "default": false, + "title": "Hangup Farewell Enabled", + "type": "boolean" }, "record_audio": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Record Audio" + "default": true, + "title": "Record Audio", + "type": "boolean" }, "timezone": { + "default": "", + "title": "Timezone", + "type": "string" + }, + "transfer_destinations": { + "default": [], + "items": { + "$ref": "#/components/schemas/AgentTransferDestination" + }, + "title": "Transfer Destinations", + "type": "array" + }, + "outbound": { "anyOf": [ { - "maxLength": 64, - "type": "string" + "$ref": "#/components/schemas/AgentOutboundConfig" }, { "type": "null" } ], - "default": null, - "title": "Timezone" + "default": null + } + }, + "title": "AgentConversationConfig", + "type": "object" + }, + "AgentGuardrailsConfig": { + "properties": { + "focus": { + "default": false, + "title": "Focus", + "type": "boolean" }, - "transfer_destinations": { + "manipulation": { + "default": false, + "title": "Manipulation", + "type": "boolean" + } + }, + "title": "AgentGuardrailsConfig", + "type": "object" + }, + "AgentLLMConfigRedacted": { + "properties": { + "model": { + "default": "google/gemini-3.6-flash", + "enum": [ + "google/gemini-3.5-flash-lite", + "google/gemini-3.6-flash", + "google/gemini-2.5-flash", + "google/gemma-4-26b-a4b-it", + "anthropic/claude-haiku-4.5", + "anthropic/claude-sonnet-4.6", + "openai/gpt-5.6-luna", + "openai/gpt-4o", + "openai/gpt-4.1" + ], + "title": "Model", + "type": "string" + }, + "custom": { "anyOf": [ { - "items": { - "$ref": "#/components/schemas/AgentTransferDestinationPatch" - }, - "type": "array" + "$ref": "#/components/schemas/AgentLLMCustomConfigRedacted" }, { "type": "null" } ], + "default": null + } + }, + "title": "AgentLLMConfigRedacted", + "type": "object" + }, + "AgentLLMCustomConfigRedacted": { + "description": "Read entity: the stored api_key never leaves the collection.", + "properties": { + "base_url": { + "minLength": 1, + "title": "Base Url", + "type": "string" + }, + "model": { + "minLength": 1, + "title": "Model", + "type": "string" + }, + "api_key": { "default": null, - "title": "Transfer Destinations" + "title": "Api Key", + "type": "null" + } + }, + "required": [ + "base_url", + "model" + ], + "title": "AgentLLMCustomConfigRedacted", + "type": "object" + }, + "AgentOutboundConfig": { + "description": "Deprecated (2026-09-07): kept for compatibility only. `amd_enabled` and\n`voicemail` no longer change call behavior; outbound calls greet as soon as\nthe callee answers, and `answered_by` is always `unknown`.", + "properties": { + "amd_enabled": { + "default": true, + "title": "Amd Enabled", + "type": "boolean" }, - "outbound": { + "voicemail": { "anyOf": [ { - "$ref": "#/components/schemas/AgentOutboundPatch" + "$ref": "#/components/schemas/AgentOutboundVoicemailConfig" }, { "type": "null" @@ -16421,307 +21030,367 @@ "default": null } }, - "title": "PublicAgentConversationPatch", + "title": "AgentOutboundConfig", "type": "object" }, - "PublicAgentCreatePayload": { - "additionalProperties": false, + "AgentOutboundVoicemailConfig": { + "description": "Deprecated (2026-09-07): answering-machine detection was removed from the\ncall runtime, so voicemail actions never fire. The node is still accepted\nand stored so existing configs and API callers keep working; the worker\nno longer receives it.", "properties": { - "name": { - "maxLength": 120, - "minLength": 1, - "title": "Name", + "action": { + "default": "hangup", + "enum": [ + "hangup", + "leave_message", + "leave_prompt_message" + ], + "title": "Action", "type": "string" }, - "description": { + "message": { "default": "", - "maxLength": 2000, - "title": "Description", + "title": "Message", + "type": "string" + } + }, + "title": "AgentOutboundVoicemailConfig", + "type": "object" + }, + "AgentPromptConfig": { + "properties": { + "system_prompt": { + "default": "", + "title": "System Prompt", "type": "string" }, - "config": { - "anyOf": [ - { - "$ref": "#/components/schemas/PublicAgentConfigPatchPayload" - }, - { - "type": "null" - } + "first_message_mode": { + "default": "prompt", + "enum": [ + "off", + "fixed", + "prompt" ], - "default": null, - "description": "Optional initial draft configuration, applied atop the platform defaults; one call provisions a fully configured agent." + "title": "First Message Mode", + "type": "string" + }, + "first_message": { + "default": "Hi! Thanks for calling — how can I help you today?", + "title": "First Message", + "type": "string" + }, + "first_message_prompt": { + "default": "Greet the caller and ask what you can help with.", + "title": "First Message Prompt", + "type": "string" } }, - "required": [ - "name" - ], - "title": "PublicAgentCreatePayload", + "title": "AgentPromptConfig", "type": "object" }, - "PublicAgentGuardrailsPatch": { - "additionalProperties": false, + "AgentSystemToolsConfig": { + "properties": { + "hang_up_call": { + "default": false, + "title": "Hang Up Call", + "type": "boolean" + } + }, + "title": "AgentSystemToolsConfig", + "type": "object" + }, + "AgentTransferDestination": { "properties": { - "focus": { + "type": { + "default": "phone", + "enum": [ + "phone", + "sip" + ], + "title": "Type", + "type": "string" + }, + "label": { + "default": "", + "title": "Label", + "type": "string" + }, + "phone_number": { "anyOf": [ { - "type": "boolean" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Focus" + "title": "Phone Number" }, - "manipulation": { + "sip_uri": { "anyOf": [ { - "type": "boolean" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Manipulation" + "title": "Sip Uri" + }, + "description": { + "default": "", + "title": "Description", + "type": "string" + }, + "mode": { + "default": "cold", + "enum": [ + "cold", + "warm" + ], + "title": "Mode", + "type": "string" + }, + "warm_connect": { + "default": "confirm", + "enum": [ + "confirm", + "briefing", + "direct" + ], + "title": "Warm Connect", + "type": "string" + }, + "warm_briefing_instructions": { + "default": "", + "title": "Warm Briefing Instructions", + "type": "string" + }, + "on_failure": { + "$ref": "#/components/schemas/AgentTransferOnFailure" } }, - "title": "PublicAgentGuardrailsPatch", + "title": "AgentTransferDestination", "type": "object" }, - "PublicAgentKnowledgeBasePatch": { - "additionalProperties": false, + "AgentTransferOnFailure": { "properties": { - "enabled": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } + "action": { + "default": "return_to_agent", + "enum": [ + "return_to_agent", + "end_call" ], - "default": null, - "description": "Whether the agent retrieves from its attached sources. Defaults to true when omitted alongside a non-empty knowledge_source_ids; send false explicitly to attach sources without using them yet.", - "title": "Enabled" + "title": "Action", + "type": "string" }, - "knowledge_source_ids": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Replaces the attached knowledge sources wholesale. Every id must be an existing knowledge source visible to the agent, else 422.", - "title": "Knowledge Source Ids" + "message": { + "default": "", + "title": "Message", + "type": "string" } }, - "title": "PublicAgentKnowledgeBasePatch", + "title": "AgentTransferOnFailure", "type": "object" }, - "PublicAgentLLMCustomConfig": { - "additionalProperties": false, + "AgentVoiceConfigView": { + "description": "Read shape of the voice section. The ASR fields that moved to the asr\nsection stay mirrored here, deprecated, so clients written before the split\nkeep reading them.", "properties": { - "base_url": { - "maxLength": 2000, - "minLength": 1, - "title": "Base Url", + "voice_id": { + "default": "b347db033a6549378b48d00acb0d06cd", + "title": "Voice Id", "type": "string" }, - "model": { - "maxLength": 256, - "minLength": 1, - "title": "Model", + "speaking_language": { + "default": "en", + "enum": [ + "en", + "ja", + "zh", + "ko", + "es", + "fr", + "de", + "pt", + "it", + "nl", + "ar", + "hi", + "id", + "tr", + "ru", + "bg", + "sr", + "hr", + "cs", + "sk", + "pl", + "uk", + "ro", + "hu", + "el", + "sv", + "da", + "no", + "fi", + "et", + "lv", + "lt", + "ca", + "he", + "fa", + "ur", + "kk", + "ka", + "hy", + "bn", + "ta", + "te", + "kn", + "mr", + "gu", + "pa", + "ne", + "th", + "vi", + "ms", + "tl", + "af" + ], + "title": "Speaking Language", "type": "string" }, - "api_key": { - "anyOf": [ - { - "maxLength": 4096, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Sent to your endpoint as the Authorization bearer token; write-only, reads return null. Required when introducing `custom`; omit to keep the stored key when updating.", - "title": "Api Key" + "expressive": { + "default": false, + "title": "Expressive", + "type": "boolean" + }, + "speed": { + "default": 1, + "maximum": 2, + "minimum": 0.5, + "title": "Speed", + "type": "number" } }, - "required": [ - "base_url", - "model" - ], - "title": "PublicAgentLLMCustomConfig", + "title": "AgentVoiceConfigView", "type": "object" }, - "PublicAgentLLMPatch": { - "additionalProperties": false, + "PublicAgentKnowledgeBaseConfig": { "properties": { - "model": { - "anyOf": [ - { - "enum": [ - "google/gemini-3.5-flash-lite", - "google/gemini-3.6-flash", - "google/gemini-2.5-flash", - "google/gemma-4-26b-a4b-it", - "anthropic/claude-haiku-4.5", - "anthropic/claude-sonnet-4.6", - "openai/gpt-5.6-luna", - "openai/gpt-4o", - "openai/gpt-4.1" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Model" + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "knowledge_source_ids": { + "items": { + "type": "string" + }, + "title": "Knowledge Source Ids", + "type": "array" + } + }, + "title": "PublicAgentKnowledgeBaseConfig", + "type": "object" + }, + "PublicAgentToolsConfig": { + "properties": { + "enabled": { + "default": true, + "title": "Enabled", + "type": "boolean" + }, + "tool_ids": { + "items": { + "type": "string" + }, + "title": "Tool Ids", + "type": "array" }, - "custom": { - "anyOf": [ - { - "$ref": "#/components/schemas/PublicAgentLLMCustomConfig" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Your own OpenAI-compatible endpoint; mutually exclusive with `model`, explicit null reverts to the platform model." + "system_tools": { + "$ref": "#/components/schemas/AgentSystemToolsConfig" } }, - "title": "PublicAgentLLMPatch", + "title": "PublicAgentToolsConfig", "type": "object" }, - "PublicAgentPromptPatch": { - "additionalProperties": false, + "PublicAgentWebhooksConfig": { "properties": { - "system_prompt": { + "conversation_init": { "anyOf": [ { - "maxLength": 500000, - "type": "string" + "$ref": "#/components/schemas/PublicConversationInitWebhook" }, { "type": "null" } ], - "default": null, - "description": "Limited to 32000 tokens (o200k_base); keeping it under 2000 tokens is recommended for latency and cost.", - "title": "System Prompt" + "default": null }, - "first_message_mode": { - "anyOf": [ - { - "enum": [ - "off", - "fixed", - "prompt" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "First Message Mode" + "post_call": { + "items": { + "$ref": "#/components/schemas/PublicPostCallWebhook" + }, + "title": "Post Call", + "type": "array" + } + }, + "title": "PublicAgentWebhooksConfig", + "type": "object" + }, + "PublicConversationInitWebhook": { + "properties": { + "url": { + "title": "Url", + "type": "string" }, - "first_message": { - "anyOf": [ - { - "maxLength": 10000, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "First Message" + "has_secret": { + "default": false, + "description": "Whether a signing secret is configured; the value itself is never returned.", + "title": "Has Secret", + "type": "boolean" }, - "first_message_prompt": { - "anyOf": [ - { - "maxLength": 10000, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "First Message Prompt" + "timeout_seconds": { + "default": 5, + "title": "Timeout Seconds", + "type": "number" } }, - "title": "PublicAgentPromptPatch", + "required": [ + "url" + ], + "title": "PublicConversationInitWebhook", "type": "object" }, - "PublicAgentToolsPatch": { - "additionalProperties": false, + "PublicPostCallWebhook": { "properties": { - "enabled": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Enabled" - }, - "tool_ids": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Replaces the attached tool set wholesale. Every id must be an existing tool in the agent's workspace, else 422.", - "title": "Tool Ids" + "url": { + "title": "Url", + "type": "string" }, - "system_tools": { - "anyOf": [ - { - "$ref": "#/components/schemas/PublicSystemToolsPatch" - }, - { - "type": "null" - } - ], - "default": null + "has_secret": { + "default": false, + "description": "Whether a signing secret is configured; the value itself is never returned.", + "title": "Has Secret", + "type": "boolean" } }, - "title": "PublicAgentToolsPatch", + "required": [ + "url" + ], + "title": "PublicPostCallWebhook", "type": "object" }, - "PublicAgentVoicePatch": { - "additionalProperties": false, + "PublicAgentVersionSummary": { "properties": { - "voice_id": { + "version_number": { + "title": "Version Number", + "type": "integer" + }, + "version_title": { "anyOf": [ { - "maxLength": 256, "type": "string" }, { @@ -16729,65 +21398,11 @@ } ], "default": null, - "title": "Voice Id" + "title": "Version Title" }, - "speaking_language": { + "version_description": { "anyOf": [ { - "enum": [ - "en", - "ja", - "zh", - "ko", - "es", - "fr", - "de", - "pt", - "it", - "nl", - "ar", - "hi", - "id", - "tr", - "ru", - "bg", - "sr", - "hr", - "cs", - "sk", - "pl", - "uk", - "ro", - "hu", - "el", - "sv", - "da", - "no", - "fi", - "et", - "lv", - "lt", - "ca", - "he", - "fa", - "ur", - "kk", - "ka", - "hy", - "bn", - "ta", - "te", - "kn", - "mr", - "gu", - "pa", - "ne", - "th", - "vi", - "ms", - "tl", - "af" - ], "type": "string" }, { @@ -16795,125 +21410,191 @@ } ], "default": null, - "title": "Speaking Language" + "title": "Version Description" + }, + "config_hash": { + "title": "Config Hash", + "type": "string" + }, + "published_at": { + "format": "date-time", + "title": "Published At", + "type": "string" + } + }, + "required": [ + "version_number", + "config_hash", + "published_at" + ], + "title": "PublicAgentVersionSummary", + "type": "object" + }, + "PublicAgentConfigEntity": { + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "config_hash": { + "description": "Content hash of the draft; equal to a version's config_hash when the draft has no unpublished changes.", + "title": "Config Hash", + "type": "string" + }, + "prompt": { + "$ref": "#/components/schemas/AgentPromptConfig" }, - "expressive": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Expressive" + "voice": { + "$ref": "#/components/schemas/AgentVoiceConfigView" }, - "speed": { - "anyOf": [ - { - "maximum": 2, - "minimum": 0.5, - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Speed" + "asr": { + "$ref": "#/components/schemas/AgentAsrConfig" + }, + "conversation": { + "$ref": "#/components/schemas/AgentConversationConfig" + }, + "tools": { + "$ref": "#/components/schemas/PublicAgentToolsConfig" + }, + "webhooks": { + "$ref": "#/components/schemas/PublicAgentWebhooksConfig" + }, + "knowledge_base": { + "$ref": "#/components/schemas/PublicAgentKnowledgeBaseConfig" + }, + "analysis": { + "$ref": "#/components/schemas/AgentAnalysisConfig" + }, + "guardrails": { + "$ref": "#/components/schemas/AgentGuardrailsConfig" + }, + "llm": { + "$ref": "#/components/schemas/AgentLLMConfigRedacted" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" } }, - "title": "PublicAgentVoicePatch", + "required": [ + "agent_id", + "config_hash", + "prompt", + "voice", + "asr", + "conversation", + "tools", + "webhooks", + "knowledge_base", + "analysis", + "guardrails", + "llm", + "updated_at" + ], + "title": "PublicAgentConfigEntity", "type": "object" }, - "PublicAgentWebhooksPatch": { - "additionalProperties": false, + "AgentTestAssertionResult": { "properties": { - "conversation_init": { + "kind": { + "enum": [ + "tool_call", + "forbidden_tool", + "ended_by" + ], + "title": "Kind", + "type": "string" + }, + "target": { + "title": "Target", + "type": "string" + }, + "tool_id": { "anyOf": [ { - "$ref": "#/components/schemas/PublicConversationInitWebhookPayload" + "type": "string" }, { "type": "null" } ], "default": null, - "description": "Pre-call webhook for inbound phone calls: called while the call is being set up; the response's dynamic_variables render into the published prompt before the agent speaks. An explicit null removes it. Takes effect after the next publish." + "title": "Tool Id" }, - "post_call": { + "tool_type": { "anyOf": [ { - "items": { - "$ref": "#/components/schemas/PublicPostCallWebhookPayload" - }, - "maxItems": 5, - "type": "array" + "enum": [ + "webhook", + "client", + "integration" + ], + "type": "string" }, { "type": "null" } ], "default": null, - "description": "Up to 5 endpoints, each receiving every post-call event. Replaces the configured list wholesale; a single object is still accepted and is stored as a one-element list, and null (or an empty list) clears every endpoint. URLs must be unique.", - "title": "Post Call" + "title": "Tool Type" + }, + "passed": { + "title": "Passed", + "type": "boolean" + }, + "detail": { + "default": "", + "title": "Detail", + "type": "string" } }, - "title": "PublicAgentWebhooksPatch", + "required": [ + "kind", + "target", + "passed" + ], + "title": "AgentTestAssertionResult", "type": "object" }, - "PublicConversationInitWebhookPayload": { - "additionalProperties": false, + "AgentTestConditionResult": { "properties": { - "url": { - "maxLength": 4000, - "minLength": 1, - "title": "Url", + "name": { + "title": "Name", "type": "string" }, - "secret": { - "anyOf": [ - { - "maxLength": 256, - "type": "string" - }, - { - "type": "null" - } + "result": { + "enum": [ + "success", + "failure", + "unknown" ], - "default": null, - "description": "Optional HMAC-SHA256 signing secret (X-Fish-Webhook-Signature). Write-only: reads report `has_secret` instead of the value. Omit it (or send null) to keep the secret already stored for the same url; send an empty string to remove it.", - "title": "Secret" + "title": "Result", + "type": "string" }, - "timeout_seconds": { - "default": 5, - "description": "How long the platform waits for the endpoint before connecting the call without variables.", - "maximum": 8, - "minimum": 1, - "title": "Timeout Seconds", - "type": "number" + "rationale": { + "default": "", + "title": "Rationale", + "type": "string" } }, "required": [ - "url" + "name", + "result" ], - "title": "PublicConversationInitWebhookPayload", + "title": "AgentTestConditionResult", "type": "object" }, - "PublicPostCallWebhookPayload": { - "additionalProperties": false, + "AgentTestMockGap": { + "description": "Calls of one tool the test prepared no answer for. A run with a\ngap never passes cleanly: it fails with needs_review.", "properties": { - "url": { - "maxLength": 4000, - "minLength": 1, - "title": "Url", + "tool_name": { + "title": "Tool Name", "type": "string" }, - "secret": { + "tool_id": { "anyOf": [ { - "maxLength": 256, "type": "string" }, { @@ -16921,43 +21602,124 @@ } ], "default": null, - "description": "Optional HMAC-SHA256 signing secret (X-Fish-Webhook-Signature). Write-only: reads report `has_secret` instead of the value. Omit it (or send null) to keep the secret already stored for the same url; send an empty string to remove it.", - "title": "Secret" - } - }, - "required": [ - "url" - ], - "title": "PublicPostCallWebhookPayload", - "type": "object" - }, - "PublicSystemToolsPatch": { - "additionalProperties": false, - "properties": { - "hang_up_call": { + "title": "Tool Id" + }, + "tool_type": { "anyOf": [ { - "type": "boolean" + "enum": [ + "webhook", + "client", + "integration" + ], + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Hang Up Call" + "title": "Tool Type" + }, + "source": { + "enum": [ + "missing", + "unmatched" + ], + "title": "Source", + "type": "string" + }, + "calls": { + "default": 0, + "title": "Calls", + "type": "integer" } }, - "title": "PublicSystemToolsPatch", + "required": [ + "tool_name", + "source" + ], + "title": "AgentTestMockGap", "type": "object" }, - "PublicAgentUpdatePayload": { - "additionalProperties": false, + "AgentTestSimulationResult": { + "description": "The simulation run's result: what was said, what the agent called,\nthe deterministic assertions and the judge's per-condition verdicts.", "properties": { - "name": { + "transcript": { + "items": { + "$ref": "#/components/schemas/AgentTestTranscriptMessage" + }, + "title": "Transcript", + "type": "array" + }, + "tool_calls": { + "items": { + "$ref": "#/components/schemas/AgentTestToolCallRecord" + }, + "title": "Tool Calls", + "type": "array" + }, + "assertions": { + "items": { + "$ref": "#/components/schemas/AgentTestAssertionResult" + }, + "title": "Assertions", + "type": "array" + }, + "conditions": { + "items": { + "$ref": "#/components/schemas/AgentTestConditionResult" + }, + "title": "Conditions", + "type": "array" + }, + "summary": { + "default": "", + "title": "Summary", + "type": "string" + }, + "passed": { + "default": false, + "title": "Passed", + "type": "boolean" + } + }, + "title": "AgentTestSimulationResult", + "type": "object" + }, + "AgentTestToolCallRecord": { + "description": "One tool the agent invoked during the simulation, as the runtime ledger recorded it.", + "properties": { + "id": { + "default": "", + "title": "Id", + "type": "string" + }, + "tool_name": { + "default": "", + "title": "Tool Name", + "type": "string" + }, + "tool_source": { + "default": "", + "title": "Tool Source", + "type": "string" + }, + "input": { + "additionalProperties": { + "$ref": "#/components/schemas/JsonValue" + }, + "title": "Input", + "type": "object" + }, + "status": { + "default": "", + "title": "Status", + "type": "string" + }, + "output": { "anyOf": [ { - "maxLength": 120, - "minLength": 1, "type": "string" }, { @@ -16965,12 +21727,11 @@ } ], "default": null, - "title": "Name" + "title": "Output" }, - "description": { + "error": { "anyOf": [ { - "maxLength": 2000, "type": "string" }, { @@ -16978,14 +21739,22 @@ } ], "default": null, - "title": "Description" + "title": "Error" }, - "status": { + "mocked": { + "default": false, + "title": "Mocked", + "type": "boolean" + }, + "mock_source": { "anyOf": [ { "enum": [ - "active", - "archived" + "test", + "tool", + "missing", + "unmatched", + "real" ], "type": "string" }, @@ -16994,166 +21763,83 @@ } ], "default": null, - "title": "Status" + "title": "Mock Source" }, - "public_enabled": { + "node_id": { + "default": "", + "title": "Node Id", + "type": "string" + }, + "turn_id": { "anyOf": [ { - "type": "boolean" + "type": "integer" }, { "type": "null" } ], "default": null, - "description": "Allow keyless session creation from the browser SDK, gated by allowed_origins.", - "title": "Public Enabled" + "title": "Turn Id" }, - "allowed_origins": { + "created_at": { "anyOf": [ { - "items": { - "type": "string" - }, - "maxItems": 20, - "type": "array" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Allowed Origins" + "title": "Created At" }, - "overrides_allowed": { + "seq": { "anyOf": [ { - "items": { - "enum": [ - "first_message", - "system_prompt", - "voice_id", - "language" - ], - "type": "string" - }, - "maxItems": 8, - "type": "array" + "type": "integer" }, { "type": "null" } ], "default": null, - "description": "Which session-creation overrides callers may use. An empty list rejects all overrides.", - "title": "Overrides Allowed" - } - }, - "title": "PublicAgentUpdatePayload", - "type": "object" - }, - "AgentAnalysisConfig": { - "properties": { - "summary": { - "$ref": "#/components/schemas/AgentAnalysisSummaryConfig" - }, - "data_fields": { - "items": { - "$ref": "#/components/schemas/AgentAnalysisDataField" - }, - "maxItems": 20, - "title": "Data Fields", - "type": "array" - }, - "criteria": { - "items": { - "$ref": "#/components/schemas/AgentAnalysisCriterion" - }, - "maxItems": 10, - "title": "Criteria", - "type": "array" - } - }, - "title": "AgentAnalysisConfig", - "type": "object" - }, - "AgentAnalysisCriterion": { - "properties": { - "name": { - "pattern": "^[a-z][a-z0-9_]{0,63}$", - "title": "Name", - "type": "string" - }, - "description": { - "default": "", - "maxLength": 500, - "title": "Description", - "type": "string" + "title": "Seq" } }, - "required": [ - "name" - ], - "title": "AgentAnalysisCriterion", + "title": "AgentTestToolCallRecord", "type": "object" }, - "AgentAnalysisDataField": { + "AgentTestTranscriptMessage": { "properties": { - "name": { - "pattern": "^[a-z][a-z0-9_]{0,63}$", - "title": "Name", - "type": "string" - }, - "type": { - "default": "text", + "role": { "enum": [ - "boolean", - "text", - "number", - "enum" + "user", + "agent" ], - "title": "Type", + "title": "Role", "type": "string" }, - "description": { + "text": { "default": "", - "maxLength": 500, - "title": "Description", + "title": "Text", "type": "string" }, - "enum_options": { + "turn_id": { "anyOf": [ { - "items": { - "type": "string" - }, - "type": "array" + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Enum Options" - } - }, - "required": [ - "name" - ], - "title": "AgentAnalysisDataField", - "type": "object" - }, - "AgentAnalysisSummaryConfig": { - "properties": { - "enabled": { - "default": true, - "title": "Enabled", - "type": "boolean" + "title": "Turn Id" }, - "prompt": { + "node_id": { "anyOf": [ { - "maxLength": 1000, "type": "string" }, { @@ -17161,672 +21847,833 @@ } ], "default": null, - "title": "Prompt" + "title": "Node Id" }, - "language": { - "default": "en", - "enum": [ - "en", - "ja", - "zh", - "ko", - "es", - "fr", - "de", - "pt", - "it", - "nl", - "ar", - "hi", - "id", - "tr", - "ru", - "bg", - "sr", - "hr", - "cs", - "sk", - "pl", - "uk", - "ro", - "hu", - "el", - "sv", - "da", - "no", - "fi", - "et", - "lv", - "lt", - "ca", - "he", - "fa", - "ur", - "kk", - "ka", - "hy", - "bn", - "ta", - "te", - "kn", - "mr", - "gu", - "pa", - "ne", - "th", - "vi", - "ms", - "tl", - "af" - ], - "title": "Language", - "type": "string" - } - }, - "title": "AgentAnalysisSummaryConfig", - "type": "object" - }, - "AgentAsrConfig": { - "properties": { - "model": { - "default": "deepgram:nova-3", - "enum": [ - "deepgram:nova-3", - "elevenlabs:scribe_v2_realtime", - "elevenlabs:scribe_v2_medical" + "created_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } ], - "title": "Model", - "type": "string" - }, - "multilingual": { - "default": false, - "title": "Multilingual", - "type": "boolean" - }, - "strict_language": { - "default": false, - "description": "Enforces voice.speaking_language whatever multilingual says: speech recognized as another language reaches the agent as [unintelligible speech]. Language is detected per utterance, so a short or heavily accented phrase in the speaking language can occasionally be detected as another language and replaced too. With deepgram:nova-3, speech in another language is usually not transcribed at all. Only enable it when you explicitly need to stop the agent from understanding other languages.", - "title": "Strict Language", - "type": "boolean" + "default": null, + "title": "Created At" }, - "keyterms": { - "items": { - "type": "string" - }, - "maxItems": 50, - "title": "Keyterms", - "type": "array" + "seq": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Seq" } }, - "title": "AgentAsrConfig", + "required": [ + "role" + ], + "title": "AgentTestTranscriptMessage", "type": "object" }, - "AgentConversationConfig": { + "PublicAgentTestRun": { "properties": { - "max_duration_seconds": { - "default": 1800, - "title": "Max Duration Seconds", - "type": "integer" + "run_id": { + "title": "Run Id", + "type": "string" }, - "response_wait_ms": { - "default": 550, - "title": "Response Wait Ms", - "type": "integer" + "test_id": { + "title": "Test Id", + "type": "string" }, - "eagerness": { - "default": "balanced", - "enum": [ - "relaxed", - "balanced", - "eager" - ], - "title": "Eagerness", + "agent_id": { + "title": "Agent Id", "type": "string" }, - "response_max_wait_ms": { + "batch_id": { "anyOf": [ { - "type": "integer" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Response Max Wait Ms" - }, - "interruptible": { - "default": true, - "title": "Interruptible", - "type": "boolean" + "title": "Batch Id" }, - "interruption_sensitivity": { - "default": "balanced", + "test_type": { "enum": [ - "low", - "balanced", - "high" + "next_reply", + "tool", + "simulation" ], - "title": "Interruption Sensitivity", + "title": "Test Type", "type": "string" }, - "interruption_ignore_phrases": { - "default": [], - "items": { - "type": "string" - }, - "title": "Interruption Ignore Phrases", - "type": "array" + "repeat_index": { + "default": 0, + "description": "Which repeat of the test in its batch this run is.", + "title": "Repeat Index", + "type": "integer" }, - "speculative_response": { - "default": true, - "title": "Speculative Response", - "type": "boolean" + "status": { + "enum": [ + "queued", + "running", + "passed", + "failed", + "error" + ], + "title": "Status", + "type": "string" }, - "reengage_enabled": { + "needs_review": { "default": false, - "title": "Reengage Enabled", + "description": "The judge could not decide, or a tool call had no mock answer.", + "title": "Needs Review", "type": "boolean" }, - "hangup_after_seconds": { - "default": 60, - "title": "Hangup After Seconds", - "type": "integer" + "error": { + "default": "", + "title": "Error", + "type": "string" }, - "hangup_farewell_enabled": { - "default": false, - "title": "Hangup Farewell Enabled", - "type": "boolean" + "usage": { + "$ref": "#/components/schemas/PublicAgentTestRunUsage" }, - "record_audio": { - "default": true, - "title": "Record Audio", - "type": "boolean" + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" }, - "timezone": { + "started_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Started At" + }, + "finished_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Finished At" + }, + "agent_reply": { "default": "", - "title": "Timezone", + "description": "The reply a scripted test judged.", + "title": "Agent Reply", "type": "string" }, - "transfer_destinations": { - "default": [], - "items": { - "$ref": "#/components/schemas/AgentTransferDestination" - }, - "title": "Transfer Destinations", - "type": "array" + "judge_reasoning": { + "default": "", + "title": "Judge Reasoning", + "type": "string" }, - "outbound": { + "config_hash": { + "default": "", + "description": "The agent draft's config hash the run tested.", + "title": "Config Hash", + "type": "string" + }, + "latency_ms": { + "default": 0, + "title": "Latency Ms", + "type": "integer" + }, + "simulation_result": { "anyOf": [ { - "$ref": "#/components/schemas/AgentOutboundConfig" + "$ref": "#/components/schemas/AgentTestSimulationResult" }, { "type": "null" } ], "default": null - } - }, - "title": "AgentConversationConfig", - "type": "object" - }, - "AgentGuardrailsConfig": { - "properties": { - "focus": { - "default": false, - "title": "Focus", - "type": "boolean" }, - "manipulation": { - "default": false, - "title": "Manipulation", - "type": "boolean" + "channel": { + "anyOf": [ + { + "enum": [ + "web_voice", + "phone_inbound", + "phone_outbound" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Channel" + }, + "turns_used": { + "default": 0, + "title": "Turns Used", + "type": "integer" + }, + "ended_by": { + "anyOf": [ + { + "enum": [ + "agent", + "user", + "transfer", + "max_turns", + "timeout", + "error" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ended By" + }, + "mock_gaps": { + "items": { + "$ref": "#/components/schemas/AgentTestMockGap" + }, + "title": "Mock Gaps", + "type": "array" } }, - "title": "AgentGuardrailsConfig", + "required": [ + "run_id", + "test_id", + "agent_id", + "test_type", + "status", + "created_at" + ], + "title": "PublicAgentTestRun", "type": "object" }, - "AgentLLMConfigRedacted": { + "PublicAgentTestRunUsage": { "properties": { - "model": { - "default": "google/gemini-3.6-flash", - "enum": [ - "google/gemini-3.5-flash-lite", - "google/gemini-3.6-flash", - "google/gemini-2.5-flash", - "google/gemma-4-26b-a4b-it", - "anthropic/claude-haiku-4.5", - "anthropic/claude-sonnet-4.6", - "openai/gpt-5.6-luna", - "openai/gpt-4o", - "openai/gpt-4.1" - ], - "title": "Model", - "type": "string" + "agent_replies": { + "default": 0, + "title": "Agent Replies", + "type": "integer" }, - "custom": { + "cost_usd": { "anyOf": [ { - "$ref": "#/components/schemas/AgentLLMCustomConfigRedacted" + "type": "string" }, { "type": "null" } ], - "default": null + "default": null, + "description": "LLM cost of the run in USD, null while any part is unpriced.", + "title": "Cost Usd" } }, - "title": "AgentLLMConfigRedacted", + "title": "PublicAgentTestRunUsage", "type": "object" }, - "AgentLLMCustomConfigRedacted": { - "description": "Read entity: the stored api_key never leaves the collection.", + "PublicAgentTestTool": { "properties": { - "base_url": { - "minLength": 1, - "title": "Base Url", + "id": { + "description": "The id to reference in tests. Webhook and client tools use their tool id, integration tools use :.", + "title": "Id", "type": "string" }, - "model": { - "minLength": 1, - "title": "Model", + "name": { + "title": "Name", "type": "string" }, - "api_key": { + "type": { + "enum": [ + "webhook", + "client", + "integration" + ], + "title": "Type", + "type": "string" + }, + "description": { + "default": "", + "title": "Description", + "type": "string" + }, + "provider_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "default": null, - "title": "Api Key", - "type": "null" + "description": "The integration provider, integration tools only.", + "title": "Provider Key" + }, + "writes": { + "default": false, + "description": "An integration tool that changes data. It cannot run for real in a test.", + "title": "Writes", + "type": "boolean" + }, + "has_own_mock": { + "default": false, + "description": "The tool answers from its own mock when a simulation mocks every tool and the test has no entry for it.", + "title": "Has Own Mock", + "type": "boolean" + }, + "expects_response": { + "default": true, + "description": "False for a client tool that does not wait for an answer, which cannot be mocked.", + "title": "Expects Response", + "type": "boolean" + }, + "available_on_phone": { + "default": true, + "description": "False for client tools, which need a client and never run on phone calls.", + "title": "Available On Phone", + "type": "boolean" } }, "required": [ - "base_url", - "model" + "id", + "name", + "type" ], - "title": "AgentLLMCustomConfigRedacted", + "title": "PublicAgentTestTool", "type": "object" - }, - "AgentOutboundConfig": { - "description": "Deprecated (2026-09-07): kept for compatibility only. `amd_enabled` and\n`voicemail` no longer change call behavior; outbound calls greet as soon as\nthe callee answers, and `answered_by` is always `unknown`.", + }, + "PublicAgentTestBatchSummary": { "properties": { - "amd_enabled": { - "default": true, - "title": "Amd Enabled", + "batch_id": { + "title": "Batch Id", + "type": "string" + }, + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "status": { + "description": "running until every run has finished.", + "enum": [ + "running", + "completed" + ], + "title": "Status", + "type": "string" + }, + "completed": { + "title": "Completed", "type": "boolean" }, - "voicemail": { + "passed": { + "title": "Passed", + "type": "integer" + }, + "failed": { + "title": "Failed", + "type": "integer" + }, + "errors": { + "title": "Errors", + "type": "integer" + }, + "pass_rate": { "anyOf": [ { - "$ref": "#/components/schemas/AgentOutboundVoicemailConfig" + "type": "number" }, { "type": "null" } ], - "default": null - } - }, - "title": "AgentOutboundConfig", - "type": "object" - }, - "AgentOutboundVoicemailConfig": { - "description": "Deprecated (2026-09-07): answering-machine detection was removed from the\ncall runtime, so voicemail actions never fire. The node is still accepted\nand stored so existing configs and API callers keep working; the worker\nno longer receives it.", - "properties": { - "action": { - "default": "hangup", + "default": null, + "description": "passed / (passed + failed), null until a run passed or failed. Error runs never judged the agent and are left out.", + "title": "Pass Rate" + }, + "run_count": { + "title": "Run Count", + "type": "integer" + }, + "trigger_source": { + "description": "Where the batch was started: the console or the API.", "enum": [ - "hangup", - "leave_message", - "leave_prompt_message" + "console", + "api" ], - "title": "Action", + "title": "Trigger Source", "type": "string" }, - "message": { - "default": "", - "title": "Message", + "created_at": { + "description": "When the first run was created.", + "format": "date-time", + "title": "Created At", "type": "string" + }, + "finished_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When the last run finished, null until the batch completed.", + "title": "Finished At" } }, - "title": "AgentOutboundVoicemailConfig", + "required": [ + "batch_id", + "agent_id", + "status", + "completed", + "passed", + "failed", + "errors", + "run_count", + "trigger_source", + "created_at" + ], + "title": "PublicAgentTestBatchSummary", "type": "object" }, - "AgentPromptConfig": { + "PublicKnowledgeSourceSummary": { "properties": { - "system_prompt": { + "knowledge_source_id": { + "title": "Knowledge Source Id", + "type": "string" + }, + "workspace_id": { + "title": "Workspace Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { "default": "", - "title": "System Prompt", + "title": "Description", "type": "string" }, - "first_message_mode": { - "default": "prompt", - "enum": [ - "off", - "fixed", - "prompt" - ], - "title": "First Message Mode", + "file_name": { + "title": "File Name", "type": "string" }, - "first_message": { - "default": "Hi! Thanks for calling — how can I help you today?", - "title": "First Message", + "size_bytes": { + "title": "Size Bytes", + "type": "integer" + }, + "chunk_count": { + "title": "Chunk Count", + "type": "integer" + }, + "revision_number": { + "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", + "title": "Revision Number", + "type": "integer" + }, + "created_at": { + "format": "date-time", + "title": "Created At", "type": "string" }, - "first_message_prompt": { - "default": "Greet the caller and ask what you can help with.", - "title": "First Message Prompt", + "updated_at": { + "format": "date-time", + "title": "Updated At", "type": "string" } }, - "title": "AgentPromptConfig", + "required": [ + "knowledge_source_id", + "workspace_id", + "name", + "file_name", + "size_bytes", + "chunk_count", + "revision_number", + "created_at", + "updated_at" + ], + "title": "PublicKnowledgeSourceSummary", "type": "object" }, - "AgentSystemToolsConfig": { + "PublicDependentAgent": { "properties": { - "hang_up_call": { - "default": false, - "title": "Hang Up Call", - "type": "boolean" + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" } }, - "title": "AgentSystemToolsConfig", + "required": [ + "agent_id", + "name" + ], + "title": "PublicDependentAgent", "type": "object" }, - "AgentTransferDestination": { + "PublicAgentToolSummary": { "properties": { - "type": { - "default": "phone", - "enum": [ - "phone", - "sip" - ], - "title": "Type", + "tool_id": { + "title": "Tool Id", "type": "string" }, - "label": { - "default": "", - "title": "Label", + "workspace_id": { + "title": "Workspace Id", "type": "string" }, - "phone_number": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Phone Number" - }, - "sip_uri": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Sip Uri" + "name": { + "title": "Name", + "type": "string" }, "description": { "default": "", "title": "Description", "type": "string" }, - "mode": { - "default": "cold", + "tool_type": { "enum": [ - "cold", - "warm" + "webhook", + "client" ], - "title": "Mode", + "title": "Tool Type", "type": "string" }, - "warm_connect": { - "default": "confirm", + "method": { "enum": [ - "confirm", - "briefing", - "direct" + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" ], - "title": "Warm Connect", + "title": "Method", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + }, + "used_by": { + "default": 0, + "description": "How many agents reference this tool in their draft configuration.", + "title": "Used By", + "type": "integer" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "format": "date-time", + "title": "Updated At", + "type": "string" + } + }, + "required": [ + "tool_id", + "workspace_id", + "name", + "tool_type", + "method", + "url", + "created_at", + "updated_at" + ], + "title": "PublicAgentToolSummary", + "type": "object" + }, + "AgentWebhookArgumentPayload": { + "properties": { + "name": { + "maxLength": 64, + "minLength": 1, + "title": "Name", "type": "string" }, - "warm_briefing_instructions": { + "description": { "default": "", - "title": "Warm Briefing Instructions", + "maxLength": 500, + "title": "Description", "type": "string" - }, - "on_failure": { - "$ref": "#/components/schemas/AgentTransferOnFailure" } }, - "title": "AgentTransferDestination", + "required": [ + "name" + ], + "title": "AgentWebhookArgumentPayload", "type": "object" }, - "AgentTransferOnFailure": { + "AgentWebhookHeaderPayload": { "properties": { - "action": { - "default": "return_to_agent", - "enum": [ - "return_to_agent", - "end_call" - ], - "title": "Action", + "name": { + "maxLength": 128, + "minLength": 1, + "title": "Name", "type": "string" }, - "message": { - "default": "", - "title": "Message", + "value": { + "maxLength": 4000, + "title": "Value", + "type": "string" + }, + "kind": { + "default": "custom", + "enum": [ + "custom", + "authorization_bearer", + "authorization_basic" + ], + "title": "Kind", "type": "string" } }, - "title": "AgentTransferOnFailure", + "required": [ + "name", + "value" + ], + "title": "AgentWebhookHeaderPayload", "type": "object" }, - "AgentVoiceConfigView": { - "description": "Read shape of the voice section. The ASR fields that moved to the asr\nsection stay mirrored here, deprecated, so clients written before the split\nkeep reading them.", + "AgentWebhookMockResponsePayload": { "properties": { - "voice_id": { - "default": "b347db033a6549378b48d00acb0d06cd", - "title": "Voice Id", + "name": { + "default": "", + "maxLength": 120, + "title": "Name", "type": "string" }, - "speaking_language": { - "default": "en", - "enum": [ - "en", - "ja", - "zh", - "ko", - "es", - "fr", - "de", - "pt", - "it", - "nl", - "ar", - "hi", - "id", - "tr", - "ru", - "bg", - "sr", - "hr", - "cs", - "sk", - "pl", - "uk", - "ro", - "hu", - "el", - "sv", - "da", - "no", - "fi", - "et", - "lv", - "lt", - "ca", - "he", - "fa", - "ur", - "kk", - "ka", - "hy", - "bn", - "ta", - "te", - "kn", - "mr", - "gu", - "pa", - "ne", - "th", - "vi", - "ms", - "tl", - "af" - ], - "title": "Speaking Language", - "type": "string" + "status_code": { + "default": 200, + "maximum": 599, + "minimum": 100, + "title": "Status Code", + "type": "integer" }, - "expressive": { - "default": false, - "title": "Expressive", - "type": "boolean" + "content_type": { + "default": "application/json", + "maxLength": 128, + "title": "Content Type", + "type": "string" }, - "speed": { - "default": 1, - "maximum": 2, - "minimum": 0.5, - "title": "Speed", - "type": "number" + "body": { + "default": "", + "maxLength": 100000, + "title": "Body", + "type": "string" } }, - "title": "AgentVoiceConfigView", + "title": "AgentWebhookMockResponsePayload", "type": "object" }, - "PublicAgentKnowledgeBaseConfig": { + "PublicAgentToolCreatePayload": { + "additionalProperties": false, + "description": "Console payload minus the folder concept (folders are console-only).", "properties": { - "enabled": { - "default": false, - "title": "Enabled", - "type": "boolean" + "name": { + "maxLength": 120, + "minLength": 1, + "title": "Name", + "type": "string" }, - "knowledge_source_ids": { + "description": { + "default": "", + "maxLength": 2000, + "title": "Description", + "type": "string" + }, + "tool_type": { + "default": "webhook", + "enum": [ + "webhook", + "client" + ], + "title": "Tool Type", + "type": "string" + }, + "arguments": { "items": { - "type": "string" + "$ref": "#/components/schemas/AgentWebhookArgumentPayload" }, - "title": "Knowledge Source Ids", + "title": "Arguments", "type": "array" - } - }, - "title": "PublicAgentKnowledgeBaseConfig", - "type": "object" - }, - "PublicAgentToolsConfig": { - "properties": { - "enabled": { - "default": true, - "title": "Enabled", - "type": "boolean" }, - "tool_ids": { + "method": { + "default": "POST", + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "title": "Method", + "type": "string" + }, + "url": { + "default": "", + "maxLength": 4000, + "title": "Url", + "type": "string" + }, + "content_type": { + "default": "application/json", + "maxLength": 128, + "title": "Content Type", + "type": "string" + }, + "body_template": { + "default": "", + "maxLength": 100000, + "title": "Body Template", + "type": "string" + }, + "headers": { "items": { - "type": "string" + "$ref": "#/components/schemas/AgentWebhookHeaderPayload" }, - "title": "Tool Ids", + "title": "Headers", "type": "array" }, - "system_tools": { - "$ref": "#/components/schemas/AgentSystemToolsConfig" - } - }, - "title": "PublicAgentToolsConfig", - "type": "object" - }, - "PublicAgentWebhooksConfig": { - "properties": { - "conversation_init": { - "anyOf": [ - { - "$ref": "#/components/schemas/PublicConversationInitWebhook" - }, - { - "type": "null" - } + "timeout_seconds": { + "default": 30, + "maximum": 300, + "minimum": 1, + "title": "Timeout Seconds", + "type": "integer" + }, + "error_handling": { + "default": "passthrough", + "enum": [ + "passthrough", + "hide" ], - "default": null + "title": "Error Handling", + "type": "string" }, - "post_call": { + "mock_responses": { "items": { - "$ref": "#/components/schemas/PublicPostCallWebhook" + "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" }, - "title": "Post Call", + "title": "Mock Responses", "type": "array" - } - }, - "title": "PublicAgentWebhooksConfig", - "type": "object" - }, - "PublicConversationInitWebhook": { - "properties": { - "url": { - "title": "Url", - "type": "string" }, - "has_secret": { - "default": false, - "description": "Whether a signing secret is configured; the value itself is never returned.", - "title": "Has Secret", + "expects_response": { + "default": true, + "title": "Expects Response", "type": "boolean" }, - "timeout_seconds": { - "default": 5, - "title": "Timeout Seconds", - "type": "number" + "execution_mode": { + "default": "blocking", + "enum": [ + "blocking", + "fire_and_forget", + "background" + ], + "title": "Execution Mode", + "type": "string" } }, "required": [ - "url" + "name" ], - "title": "PublicConversationInitWebhook", + "title": "PublicAgentToolCreatePayload", "type": "object" }, - "PublicPostCallWebhook": { + "PublicToolHeader": { "properties": { - "url": { - "title": "Url", + "name": { + "title": "Name", + "type": "string" + }, + "kind": { + "enum": [ + "custom", + "authorization_bearer", + "authorization_basic" + ], + "title": "Kind", "type": "string" }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Header value; null for credential kinds (authorization_bearer / authorization_basic), which are write-only.", + "title": "Value" + }, "has_secret": { "default": false, - "description": "Whether a signing secret is configured; the value itself is never returned.", + "description": "Whether a credential value is stored; the value itself is never returned.", "title": "Has Secret", "type": "boolean" } }, "required": [ - "url" + "name", + "kind" ], - "title": "PublicPostCallWebhook", + "title": "PublicToolHeader", "type": "object" }, - "PublicAgentVersionSummary": { + "PublicAgentToolUpdatePayload": { + "additionalProperties": false, "properties": { - "version_number": { - "title": "Version Number", - "type": "integer" + "name": { + "anyOf": [ + { + "maxLength": 120, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" }, - "version_title": { + "description": { "anyOf": [ { + "maxLength": 2000, "type": "string" }, { @@ -17834,11 +22681,162 @@ } ], "default": null, - "title": "Version Title" + "title": "Description" }, - "version_description": { + "arguments": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/AgentWebhookArgumentPayload" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Arguments" + }, + "method": { + "anyOf": [ + { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Method" + }, + "url": { + "anyOf": [ + { + "maxLength": 4000, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Url" + }, + "content_type": { + "anyOf": [ + { + "maxLength": 128, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Content Type" + }, + "body_template": { + "anyOf": [ + { + "maxLength": 100000, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Body Template" + }, + "headers": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/AgentWebhookHeaderPayload" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Headers" + }, + "timeout_seconds": { + "anyOf": [ + { + "maximum": 300, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Timeout Seconds" + }, + "error_handling": { + "anyOf": [ + { + "enum": [ + "passthrough", + "hide" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Error Handling" + }, + "mock_responses": { "anyOf": [ { + "items": { + "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Mock Responses" + }, + "expects_response": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Expects Response" + }, + "execution_mode": { + "anyOf": [ + { + "enum": [ + "blocking", + "fire_and_forget", + "background" + ], "type": "string" }, { @@ -17846,95 +22844,16 @@ } ], "default": null, - "title": "Version Description" - }, - "config_hash": { - "title": "Config Hash", - "type": "string" - }, - "published_at": { - "format": "date-time", - "title": "Published At", - "type": "string" - } - }, - "required": [ - "version_number", - "config_hash", - "published_at" - ], - "title": "PublicAgentVersionSummary", - "type": "object" - }, - "PublicAgentConfigEntity": { - "properties": { - "agent_id": { - "title": "Agent Id", - "type": "string" - }, - "config_hash": { - "description": "Content hash of the draft; equal to a version's config_hash when the draft has no unpublished changes.", - "title": "Config Hash", - "type": "string" - }, - "prompt": { - "$ref": "#/components/schemas/AgentPromptConfig" - }, - "voice": { - "$ref": "#/components/schemas/AgentVoiceConfigView" - }, - "asr": { - "$ref": "#/components/schemas/AgentAsrConfig" - }, - "conversation": { - "$ref": "#/components/schemas/AgentConversationConfig" - }, - "tools": { - "$ref": "#/components/schemas/PublicAgentToolsConfig" - }, - "webhooks": { - "$ref": "#/components/schemas/PublicAgentWebhooksConfig" - }, - "knowledge_base": { - "$ref": "#/components/schemas/PublicAgentKnowledgeBaseConfig" - }, - "analysis": { - "$ref": "#/components/schemas/AgentAnalysisConfig" - }, - "guardrails": { - "$ref": "#/components/schemas/AgentGuardrailsConfig" - }, - "llm": { - "$ref": "#/components/schemas/AgentLLMConfigRedacted" - }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" + "title": "Execution Mode" } }, - "required": [ - "agent_id", - "config_hash", - "prompt", - "voice", - "asr", - "conversation", - "tools", - "webhooks", - "knowledge_base", - "analysis", - "guardrails", - "llm", - "updated_at" - ], - "title": "PublicAgentConfigEntity", + "title": "PublicAgentToolUpdatePayload", "type": "object" }, - "PublicKnowledgeSourceSummary": { + "PublicAgentTestSummary": { "properties": { - "knowledge_source_id": { - "title": "Knowledge Source Id", + "test_id": { + "title": "Test Id", "type": "string" }, "workspace_id": { @@ -17945,27 +22864,22 @@ "title": "Name", "type": "string" }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "file_name": { - "title": "File Name", + "test_type": { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "title": "Test Type", "type": "string" }, - "size_bytes": { - "title": "Size Bytes", - "type": "integer" - }, - "chunk_count": { - "title": "Chunk Count", - "type": "integer" - }, - "revision_number": { - "description": "Increments every time the content is replaced. Drafts always use the newest revision; published versions pin the revision current at publish time.", - "title": "Revision Number", - "type": "integer" + "agent_ids": { + "description": "Agents this test is attached to.", + "items": { + "type": "string" + }, + "title": "Agent Ids", + "type": "array" }, "created_at": { "format": "date-time", @@ -17979,355 +22893,585 @@ } }, "required": [ - "knowledge_source_id", + "test_id", "workspace_id", "name", - "file_name", - "size_bytes", - "chunk_count", - "revision_number", + "test_type", "created_at", "updated_at" ], - "title": "PublicKnowledgeSourceSummary", + "title": "PublicAgentTestSummary", "type": "object" }, - "PublicDependentAgent": { + "AgentTestMessagePayload": { "properties": { - "agent_id": { - "title": "Agent Id", + "role": { + "enum": [ + "agent", + "user" + ], + "title": "Role", "type": "string" }, - "name": { - "title": "Name", + "text": { + "maxLength": 2000, + "minLength": 1, + "title": "Text", "type": "string" } }, "required": [ - "agent_id", - "name" + "role", + "text" ], - "title": "PublicDependentAgent", + "title": "AgentTestMessagePayload", "type": "object" }, - "PublicAgentToolSummary": { + "AgentTestParamMatcher": { + "additionalProperties": false, + "description": "How one tool argument is compared: exact string equality, a regular\nexpression searched in the value, or mere presence. Regexes are\nJavaScript without flags, mock conditions run in the runtime as written\nand assertions are translated to Python for scoring.", "properties": { - "tool_id": { - "title": "Tool Id", + "type": { + "default": "exact", + "enum": [ + "exact", + "regex", + "any" + ], + "title": "Type", "type": "string" }, - "workspace_id": { - "title": "Workspace Id", + "value": { + "anyOf": [ + { + "maxLength": 1000, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Value" + } + }, + "title": "AgentTestParamMatcher", + "type": "object" + }, + "AgentTestReferencedTool": { + "additionalProperties": false, + "description": "Tool the runner should (or should not) observe on the next turn. A\nwebhook or client tool is referenced by its library id, an integration\ntool by \":\" (for example\n\"google_calendar:create_event\").", + "properties": { + "id": { + "maxLength": 64, + "minLength": 1, + "title": "Id", "type": "string" }, "name": { + "maxLength": 200, + "minLength": 1, "title": "Name", "type": "string" }, - "description": { - "default": "", - "title": "Description", - "type": "string" - }, - "tool_type": { + "type": { + "default": "webhook", "enum": [ "webhook", - "client" + "client", + "integration" ], - "title": "Tool Type", + "title": "Type", "type": "string" }, - "method": { - "enum": [ - "GET", - "POST", - "PUT", - "PATCH", - "DELETE" - ], - "title": "Method", + "description": { + "default": "", + "maxLength": 2000, + "title": "Description", "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "title": "AgentTestReferencedTool", + "type": "object" + }, + "AgentTestSimulationAssertions": { + "additionalProperties": false, + "description": "Deterministic checks run before the judge, no LLM involved.", + "properties": { + "tool_calls": { + "items": { + "$ref": "#/components/schemas/AgentTestToolCallAssertion" + }, + "maxItems": 100, + "title": "Tool Calls", + "type": "array" }, - "url": { - "title": "Url", + "forbidden_tools": { + "items": { + "$ref": "#/components/schemas/AgentTestReferencedTool" + }, + "maxItems": 100, + "title": "Forbidden Tools", + "type": "array" + }, + "ended_by": { + "anyOf": [ + { + "enum": [ + "agent", + "user", + "transfer", + "any" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ended By" + } + }, + "title": "AgentTestSimulationAssertions", + "type": "object" + }, + "AgentTestSimulationConfig": { + "additionalProperties": false, + "properties": { + "scenario": { + "maxLength": 10000, + "minLength": 1, + "title": "Scenario", "type": "string" }, - "used_by": { - "default": 0, - "description": "How many agents reference this tool in their draft configuration.", - "title": "Used By", + "max_turns": { + "default": 10, + "maximum": 50, + "minimum": 1, + "title": "Max Turns", "type": "integer" }, - "created_at": { - "format": "date-time", - "title": "Created At", - "type": "string" + "success_conditions": { + "items": { + "$ref": "#/components/schemas/AgentTestSuccessCondition" + }, + "maxItems": 10, + "minItems": 1, + "title": "Success Conditions", + "type": "array" + }, + "assertions": { + "$ref": "#/components/schemas/AgentTestSimulationAssertions" + }, + "tool_mocks": { + "$ref": "#/components/schemas/AgentTestToolMocks" + }, + "simulated_user_model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Simulated User Model" }, - "updated_at": { - "format": "date-time", - "title": "Updated At", - "type": "string" + "repeat_count": { + "default": 1, + "maximum": 20, + "minimum": 1, + "title": "Repeat Count", + "type": "integer" + }, + "channel": { + "anyOf": [ + { + "enum": [ + "web_voice", + "phone_inbound", + "phone_outbound" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Channel" } }, "required": [ - "tool_id", - "workspace_id", - "name", - "tool_type", - "method", - "url", - "created_at", - "updated_at" + "scenario", + "success_conditions" ], - "title": "PublicAgentToolSummary", + "title": "AgentTestSimulationConfig", "type": "object" }, - "AgentWebhookArgumentPayload": { + "AgentTestSuccessCondition": { + "additionalProperties": false, + "description": "One natural-language criterion the judge scores on the whole conversation.", "properties": { "name": { - "maxLength": 64, + "maxLength": 80, "minLength": 1, "title": "Name", "type": "string" }, "description": { - "default": "", "maxLength": 500, + "minLength": 1, "title": "Description", "type": "string" } }, "required": [ - "name" + "name", + "description" ], - "title": "AgentWebhookArgumentPayload", + "title": "AgentTestSuccessCondition", "type": "object" }, - "AgentWebhookHeaderPayload": { + "AgentTestToolCallAssertion": { + "additionalProperties": false, + "description": "The agent must have called this library tool, optionally with matching\narguments, between min_calls and max_calls times (calls that match).", "properties": { - "name": { - "maxLength": 128, - "minLength": 1, - "title": "Name", - "type": "string" + "tool": { + "$ref": "#/components/schemas/AgentTestReferencedTool" }, - "value": { - "maxLength": 4000, - "title": "Value", - "type": "string" + "params": { + "additionalProperties": { + "$ref": "#/components/schemas/AgentTestParamMatcher" + }, + "title": "Params", + "type": "object" }, - "kind": { - "default": "custom", - "enum": [ - "custom", - "authorization_bearer", - "authorization_basic" + "min_calls": { + "default": 1, + "maximum": 100, + "minimum": 0, + "title": "Min Calls", + "type": "integer" + }, + "max_calls": { + "anyOf": [ + { + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } ], - "title": "Kind", - "type": "string" + "default": null, + "title": "Max Calls" } }, "required": [ - "name", - "value" + "tool" ], - "title": "AgentWebhookHeaderPayload", + "title": "AgentTestToolCallAssertion", "type": "object" }, - "AgentWebhookMockResponsePayload": { + "AgentTestToolMock": { + "additionalProperties": false, + "description": "A canned result for one library tool, optionally gated on the call arguments.", "properties": { - "name": { - "default": "", - "maxLength": 120, - "title": "Name", - "type": "string" + "tool": { + "$ref": "#/components/schemas/AgentTestReferencedTool" }, - "status_code": { - "default": 200, - "maximum": 599, - "minimum": 100, - "title": "Status Code", - "type": "integer" + "result": { + "$ref": "#/components/schemas/JsonValue", + "default": null }, - "content_type": { - "default": "application/json", - "maxLength": 128, - "title": "Content Type", - "type": "string" + "status": { + "anyOf": [ + { + "maximum": 599, + "minimum": 100, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Status" }, - "body": { - "default": "", - "maxLength": 100000, - "title": "Body", - "type": "string" + "when": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/components/schemas/AgentTestParamMatcher" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "When" + }, + "error": { + "default": false, + "title": "Error", + "type": "boolean" } }, - "title": "AgentWebhookMockResponsePayload", + "required": [ + "tool" + ], + "title": "AgentTestToolMock", "type": "object" }, - "PublicAgentToolCreatePayload": { + "AgentTestToolMocks": { "additionalProperties": false, - "description": "Console payload minus the folder concept (folders are console-only).", + "description": "all: every tool answers from a mock, except real_tools, which run for\nreal. selected: only the listed tools do, the rest follow fallback.\nnone: every tool hits its real endpoint.", "properties": { - "name": { - "maxLength": 120, - "minLength": 1, - "title": "Name", - "type": "string" - }, - "description": { - "default": "", - "maxLength": 2000, - "title": "Description", - "type": "string" - }, - "tool_type": { - "default": "webhook", + "strategy": { + "default": "all", "enum": [ - "webhook", - "client" + "all", + "selected", + "none" ], - "title": "Tool Type", + "title": "Strategy", "type": "string" }, - "arguments": { - "items": { - "$ref": "#/components/schemas/AgentWebhookArgumentPayload" - }, - "title": "Arguments", - "type": "array" - }, - "method": { - "default": "POST", + "fallback": { + "default": "error", "enum": [ - "GET", - "POST", - "PUT", - "PATCH", - "DELETE" + "error", + "real" ], - "title": "Method", - "type": "string" - }, - "url": { - "default": "", - "maxLength": 4000, - "title": "Url", - "type": "string" - }, - "content_type": { - "default": "application/json", - "maxLength": 128, - "title": "Content Type", - "type": "string" - }, - "body_template": { - "default": "", - "maxLength": 100000, - "title": "Body Template", + "title": "Fallback", "type": "string" }, - "headers": { + "tools": { "items": { - "$ref": "#/components/schemas/AgentWebhookHeaderPayload" + "$ref": "#/components/schemas/AgentTestToolMock" }, - "title": "Headers", + "maxItems": 50, + "title": "Tools", "type": "array" }, - "timeout_seconds": { - "default": 30, - "maximum": 300, - "minimum": 1, - "title": "Timeout Seconds", - "type": "integer" - }, - "error_handling": { - "default": "passthrough", - "enum": [ - "passthrough", - "hide" - ], - "title": "Error Handling", - "type": "string" - }, - "mock_responses": { + "real_tools": { "items": { - "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" + "$ref": "#/components/schemas/AgentTestReferencedTool" }, - "title": "Mock Responses", + "maxItems": 50, + "title": "Real Tools", "type": "array" + } + }, + "title": "AgentTestToolMocks", + "type": "object" + }, + "AgentTestToolParameter": { + "additionalProperties": false, + "properties": { + "name": { + "maxLength": 100, + "minLength": 1, + "title": "Name", + "type": "string" }, - "expects_response": { - "default": true, - "title": "Expects Response", - "type": "boolean" + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "title": "Value" }, - "execution_mode": { - "default": "blocking", - "enum": [ - "blocking", - "fire_and_forget", - "background" + "type": { + "anyOf": [ + { + "enum": [ + "string", + "number", + "boolean" + ], + "type": "string" + }, + { + "type": "null" + } ], - "title": "Execution Mode", - "type": "string" + "default": null, + "title": "Type" } }, "required": [ - "name" + "name", + "value" ], - "title": "PublicAgentToolCreatePayload", + "title": "AgentTestToolParameter", "type": "object" }, - "PublicToolHeader": { + "PublicAgentTestCreatePayload": { + "additionalProperties": false, "properties": { "name": { + "maxLength": 200, + "minLength": 1, "title": "Name", "type": "string" }, - "kind": { + "test_type": { + "default": "next_reply", "enum": [ - "custom", - "authorization_bearer", - "authorization_basic" + "next_reply", + "tool", + "simulation" ], - "title": "Kind", + "title": "Test Type", "type": "string" }, - "value": { + "conversation": { + "items": { + "$ref": "#/components/schemas/AgentTestMessagePayload" + }, + "title": "Conversation", + "type": "array" + }, + "expectation": { + "default": "", + "maxLength": 400, + "title": "Expectation", + "type": "string" + }, + "success_examples": { + "items": { + "type": "string" + }, + "title": "Success Examples", + "type": "array" + }, + "failure_examples": { + "items": { + "type": "string" + }, + "title": "Failure Examples", + "type": "array" + }, + "referenced_tool": { "anyOf": [ { - "type": "string" + "$ref": "#/components/schemas/AgentTestReferencedTool" }, { "type": "null" } ], - "default": null, - "description": "Header value; null for credential kinds (authorization_bearer / authorization_basic), which are write-only.", - "title": "Value" + "default": null }, - "has_secret": { + "tool_parameters": { + "items": { + "$ref": "#/components/schemas/AgentTestToolParameter" + }, + "title": "Tool Parameters", + "type": "array" + }, + "verify_absence": { "default": false, - "description": "Whether a credential value is stored; the value itself is never returned.", - "title": "Has Secret", + "title": "Verify Absence", "type": "boolean" + }, + "simulation": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentTestSimulationConfig" + }, + { + "type": "null" + } + ], + "default": null + }, + "dynamic_variables": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + "title": "Dynamic Variables", + "type": "object" + }, + "agent_ids": { + "description": "Agents to attach the test to. They must all be in one workspace, and the test is created there. Without agents the test goes into the API key owner's default workspace.", + "items": { + "type": "string" + }, + "maxItems": 100, + "title": "Agent Ids", + "type": "array" } }, "required": [ - "name", - "kind" + "name" ], - "title": "PublicToolHeader", + "title": "PublicAgentTestCreatePayload", "type": "object" }, - "PublicAgentToolUpdatePayload": { + "PublicAgentTestMessage": { + "properties": { + "role": { + "enum": [ + "agent", + "user" + ], + "title": "Role", + "type": "string" + }, + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "role", + "text" + ], + "title": "PublicAgentTestMessage", + "type": "object" + }, + "PublicAgentTestUpdatePayload": { "additionalProperties": false, "properties": { "name": { "anyOf": [ { - "maxLength": 120, + "maxLength": 200, "minLength": 1, "type": "string" }, @@ -18338,10 +23482,14 @@ "default": null, "title": "Name" }, - "description": { + "test_type": { "anyOf": [ { - "maxLength": 2000, + "enum": [ + "next_reply", + "tool", + "simulation" + ], "type": "string" }, { @@ -18349,13 +23497,13 @@ } ], "default": null, - "title": "Description" + "title": "Test Type" }, - "arguments": { + "conversation": { "anyOf": [ { "items": { - "$ref": "#/components/schemas/AgentWebhookArgumentPayload" + "$ref": "#/components/schemas/AgentTestMessagePayload" }, "type": "array" }, @@ -18364,18 +23512,12 @@ } ], "default": null, - "title": "Arguments" + "title": "Conversation" }, - "method": { + "expectation": { "anyOf": [ { - "enum": [ - "GET", - "POST", - "PUT", - "PATCH", - "DELETE" - ], + "maxLength": 400, "type": "string" }, { @@ -18383,53 +23525,54 @@ } ], "default": null, - "title": "Method" + "title": "Expectation" }, - "url": { + "success_examples": { "anyOf": [ { - "maxLength": 4000, - "minLength": 1, - "type": "string" + "items": { + "type": "string" + }, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Url" + "title": "Success Examples" }, - "content_type": { + "failure_examples": { "anyOf": [ { - "maxLength": 128, - "type": "string" + "items": { + "type": "string" + }, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Content Type" + "title": "Failure Examples" }, - "body_template": { + "referenced_tool": { "anyOf": [ { - "maxLength": 100000, - "type": "string" + "$ref": "#/components/schemas/AgentTestReferencedTool" }, { "type": "null" } ], - "default": null, - "title": "Body Template" + "default": null }, - "headers": { + "tool_parameters": { "anyOf": [ { "items": { - "$ref": "#/components/schemas/AgentWebhookHeaderPayload" + "$ref": "#/components/schemas/AgentTestToolParameter" }, "type": "array" }, @@ -18438,73 +23581,138 @@ } ], "default": null, - "title": "Headers" + "title": "Tool Parameters" }, - "timeout_seconds": { + "verify_absence": { "anyOf": [ { - "maximum": 300, - "minimum": 1, - "type": "integer" + "type": "boolean" }, { "type": "null" } ], "default": null, - "title": "Timeout Seconds" + "title": "Verify Absence" }, - "error_handling": { + "simulation": { "anyOf": [ { - "enum": [ - "passthrough", - "hide" - ], - "type": "string" + "$ref": "#/components/schemas/AgentTestSimulationConfig" }, { "type": "null" } ], - "default": null, - "title": "Error Handling" + "default": null }, - "mock_responses": { + "dynamic_variables": { "anyOf": [ { - "items": { - "$ref": "#/components/schemas/AgentWebhookMockResponsePayload" + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] }, - "type": "array" + "type": "object" }, { "type": "null" } ], "default": null, - "title": "Mock Responses" + "title": "Dynamic Variables" + } + }, + "title": "PublicAgentTestUpdatePayload", + "type": "object" + }, + "PublicAgentTestRunSummary": { + "properties": { + "run_id": { + "title": "Run Id", + "type": "string" }, - "expects_response": { + "test_id": { + "title": "Test Id", + "type": "string" + }, + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "batch_id": { "anyOf": [ { - "type": "boolean" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Expects Response" + "title": "Batch Id" }, - "execution_mode": { + "test_type": { + "enum": [ + "next_reply", + "tool", + "simulation" + ], + "title": "Test Type", + "type": "string" + }, + "repeat_index": { + "default": 0, + "description": "Which repeat of the test in its batch this run is.", + "title": "Repeat Index", + "type": "integer" + }, + "status": { + "enum": [ + "queued", + "running", + "passed", + "failed", + "error" + ], + "title": "Status", + "type": "string" + }, + "needs_review": { + "default": false, + "description": "The judge could not decide, or a tool call had no mock answer.", + "title": "Needs Review", + "type": "boolean" + }, + "error": { + "default": "", + "title": "Error", + "type": "string" + }, + "usage": { + "$ref": "#/components/schemas/PublicAgentTestRunUsage" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "started_at": { "anyOf": [ { - "enum": [ - "blocking", - "fire_and_forget", - "background" - ], + "format": "date-time", "type": "string" }, { @@ -18512,10 +23720,31 @@ } ], "default": null, - "title": "Execution Mode" + "title": "Started At" + }, + "finished_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Finished At" } }, - "title": "PublicAgentToolUpdatePayload", + "required": [ + "run_id", + "test_id", + "agent_id", + "test_type", + "status", + "created_at" + ], + "title": "PublicAgentTestRunSummary", "type": "object" }, "PublicPhoneNumberEntity": { diff --git a/docs.json b/docs.json index 0a83f93..7741c0e 100644 --- a/docs.json +++ b/docs.json @@ -343,6 +343,25 @@ "api-reference/endpoint/agent/list-agents-using-tool" ] }, + { + "group": "Tests", + "icon": "flask", + "pages": [ + "api-reference/endpoint/agent/list-tests", + "api-reference/endpoint/agent/create-test", + "api-reference/endpoint/agent/get-test", + "api-reference/endpoint/agent/update-test", + "api-reference/endpoint/agent/delete-test", + "api-reference/endpoint/agent/attach-test", + "api-reference/endpoint/agent/detach-test", + "api-reference/endpoint/agent/list-test-tools", + "api-reference/endpoint/agent/run-tests", + "api-reference/endpoint/agent/list-test-batches", + "api-reference/endpoint/agent/get-test-batch", + "api-reference/endpoint/agent/list-test-runs", + "api-reference/endpoint/agent/get-test-run" + ] + }, { "group": "Knowledge Sources", "icon": "book",