{
  "components": {
    "responses": {
      "Conflict": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "The request conflicts with the resource's current state."
      },
      "NotFound": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "No such resource for this account."
      },
      "RateLimited": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "Too many requests. Retry after the indicated delay."
      },
      "Unauthorized": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "The credential is missing or invalid."
      },
      "ValidationFailed": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "One or more fields are invalid; see `errors`."
      }
    },
    "schemas": {
      "Agent": {
        "properties": {
          "autonomy": {
            "description": "Who holds a `conversation` agent's end of the call. `agentic` is a model that searches the agent's knowledge bases before it answers and calls the tools in `tools`. `simple` is not a model at all: a person takes the call in their browser, and no speech recognizer, model or synthesizer is opened for it — the audio passes between the phone and the operator's WebSocket and nothing on this platform listens to it. An inbound number pointed at a simple agent rings rather than answers, and is picked up with `POST /v1/calls/{id}/claim`, exactly as a `webrtc` call is. `custom` is a model doing exactly what `capabilities` switches on, for the lines between the two — read the documents but never write to a system, call the functions but answer from what it was told, never offer a transfer because there is nobody behind it. Meaningless on the other three modes, which never reach a model. Defaults to agentic. Note that `simple` changed meaning: it used to mean a model that talks without retrieval or functions, which is `custom` with `search_knowledge` and `call_functions` off. Agents stored under the old meaning are read as exactly that and are returned with `autonomy` of `custom`.",
            "enum": [
              "agentic",
              "simple",
              "custom"
            ],
            "type": "string"
          },
          "capabilities": {
            "description": "What the agent may do besides talk, switch by switch. Read only when `autonomy` is `custom` — `agentic` resolves from its own name and `simple` has no model on the call to grant anything to. It is stored and returned on every agent whatever the autonomy is, so trying another one and coming back does not lose what you set. Sending it replaces the whole block. Omitted on a `custom` agent it means call control and nothing else — talking, plus hanging up and reaching a person — because four absent booleans are a block nobody wrote, not a decision to build a line that cannot even end its own call.",
            "properties": {
              "call_functions": {
                "description": "Call the functions in `tools`. A master switch over that list, not a second copy of it — which functions the agent has is still `tools`.",
                "type": "boolean"
              },
              "end_call": {
                "description": "Offer `end_call`, the other tool the runtime executes itself.",
                "type": "boolean"
              },
              "max_tool_rounds": {
                "description": "How many times one turn may call a tool and generate again. 1–10, or 0 for the default of 3. Every round is a model round trip with the caller listening to the filler phrase.",
                "type": "integer"
              },
              "retrieval_timeout_ms": {
                "description": "How long a knowledge lookup may take inside the turn. 50–2000, or 0 for the deployment's setting and then 250ms. Past it the turn answers ungrounded rather than leaving the line silent.",
                "type": "integer"
              },
              "search_knowledge": {
                "description": "Search the bases in `knowledge_base_ids` during a turn, and offer `search_knowledge_base`. Off, no lookup happens and none can time out.",
                "type": "boolean"
              },
              "transfer": {
                "description": "Offer `transfer_call`. Separate from `call_functions` because the runtime executes it against the call itself, not against one of your systems.",
                "type": "boolean"
              }
            },
            "type": "object"
          },
          "conversation": {
            "description": "How the agent listens and when it gives up. Replaces the whole block when sent; every 0 inside it means the default, which is what a good phone agent does unasked.",
            "properties": {
              "interrupt_phrases": {
                "description": "Extra words or short phrases that interrupt the agent on their own, on top of the built-in list. Matched against the whole utterance.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "interrupt_words": {
                "description": "How many words the caller has to say before the agent stops talking. Below it, a \"hmm\" or an \"okay\" over the agent is the caller listening, not taking the floor. A stop word (wait, no, ruko) interrupts on its own, and so does a caller who keeps talking. 1–10; 0 means 2.",
                "type": "integer"
              },
              "max_call_seconds": {
                "description": "Ends the call, after a goodbye, once it has run this long. 30–7200; 0 leaves it to the carrier's own limit.",
                "type": "integer"
              },
              "silence_hangup_seconds": {
                "description": "How long the line may be quiet before the agent says goodbye and hangs up, measured from the same moment. 5–300 and longer than silence_prompt_seconds; 0 means 20; -1 never hangs up for silence.",
                "type": "integer"
              },
              "silence_prompt_seconds": {
                "description": "How long the line may be quiet before the agent asks whether the caller is still there. 3–60; 0 means 8; -1 never asks.",
                "type": "integer"
              },
              "voicemail": {
                "description": "What an outbound call does when the carrier reports an answering machine. continue (the default) carries on and the record says voicemail; hangup ends the call at once and says nothing; message says voicemail_message and hangs up.",
                "enum": [
                  "continue",
                  "hangup",
                  "message"
                ],
                "type": "string"
              },
              "voicemail_message": {
                "description": "What \"message\" leaves on the machine. Supports {{variables}}. At most 2000 characters.",
                "type": "string"
              }
            },
            "type": "object"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "What this agent is for.",
            "type": "string"
          },
          "filler_phrase": {
            "description": "Spoken while a tool runs, to avoid dead air.",
            "type": "string"
          },
          "flow": {
            "description": "A conversation flow: the nodes a call moves between and the edges that move it. The agent's prompt — the opening language's system_prompt — is the global instruction every node is read under. Send {} to take the flow away. Validated in full; every problem is reported at once under flow.\u003cpath\u003e.",
            "properties": {
              "nodes": {
                "items": {
                  "properties": {
                    "description": {
                      "description": "What the node is for. Read by the router as the objective when the node has no prompt.",
                      "type": "string"
                    },
                    "edges": {
                      "items": {
                        "properties": {
                          "capture": {
                            "additionalProperties": {
                              "type": "string"
                            },
                            "description": "Values the router extracts from the caller's words when this intent edge fires, keyed by name with a JSON type — {\"amount\": \"number\"}. Stored as call variables; every one is required for the edge to fire.",
                            "type": "object"
                          },
                          "event": {
                            "description": "The injected event an event edge waits for.",
                            "type": "string"
                          },
                          "expression": {
                            "description": "What an expression edge evaluates. Flat: one logic word over a list of conditions.",
                            "properties": {
                              "conditions": {
                                "items": {
                                  "properties": {
                                    "operator": {
                                      "description": "How to compare.",
                                      "enum": [
                                        "eq",
                                        "neq",
                                        "gt",
                                        "gte",
                                        "lt",
                                        "lte",
                                        "in",
                                        "not_in",
                                        "contains",
                                        "exists",
                                        "not_exists"
                                      ],
                                      "type": "string"
                                    },
                                    "value": {
                                      "description": "What to compare against. Unused by exists and not_exists."
                                    },
                                    "variable": {
                                      "description": "A dotted path into the call's variables: amount, caller.city, _node_turns.",
                                      "type": "string"
                                    }
                                  },
                                  "required": [
                                    "variable",
                                    "operator"
                                  ],
                                  "type": "object"
                                },
                                "type": "array"
                              },
                              "logic": {
                                "description": "and (the default) or or.",
                                "enum": [
                                  "and",
                                  "or"
                                ],
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "priority": {
                            "description": "Orders edges of the same type; lower fires first.",
                            "type": "integer"
                          },
                          "to": {
                            "description": "The id of the node this edge leads to.",
                            "type": "string"
                          },
                          "type": {
                            "description": "How the edge decides: intent (the model, from what the caller said — the default), expression (evaluated first, free), always (the catch-all), event (only when an outside system injects the named event).",
                            "enum": [
                              "intent",
                              "expression",
                              "always",
                              "event"
                            ],
                            "type": "string"
                          },
                          "when": {
                            "description": "The condition in a sentence. What the router reads on an intent edge; a label on the others.",
                            "type": "string"
                          }
                        },
                        "required": [
                          "to"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "examples": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "One example reply per language code, appended to the prompt.",
                      "type": "object"
                    },
                    "id": {
                      "description": "Names the node. Unique.",
                      "type": "string"
                    },
                    "knowledge_base_ids": {
                      "description": "The bases this node grounds against, overriding the agent's. Empty means no grounding here.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "prompt": {
                      "description": "This node's instructions, appended to the agent's system prompt while the call is here.",
                      "type": "string"
                    },
                    "repeat_after_silence_seconds": {
                      "description": "Re-prompt a caller who has said nothing for this long while the call sits here. 0 never re-prompts from this node.",
                      "type": "integer"
                    },
                    "say": {
                      "description": "What a static node says, exactly as written, with {{variables}} filled.",
                      "type": "string"
                    },
                    "say_in": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "The same line per language code, for an agent that speaks more than one.",
                      "type": "object"
                    },
                    "tool": {
                      "description": "Forces the model to call this tool while the call is on this node, until it has run once on this visit.",
                      "type": "string"
                    },
                    "tools": {
                      "description": "Restricts the tools the model sees here. Absent means all of the agent's; empty means none.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "type": {
                      "description": "llm (the model answers under the agent's prompt plus this node's — the default), static (says a fixed line, no model) or router (never speaks; moves on in the same turn).",
                      "enum": [
                        "llm",
                        "static",
                        "router"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "id"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "routing": {
                "description": "Tunes the model that chooses between a node's intent edges.",
                "properties": {
                  "instructions": {
                    "description": "Replaces the built-in routing instructions. Rarely needed.",
                    "type": "string"
                  },
                  "model": {
                    "description": "Overrides the agent's model for routing. A small fast one is right: the decision is one function call from a short prompt.",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "start": {
                "description": "The id of the node a call opens on.",
                "type": "string"
              },
              "variables": {
                "additionalProperties": {
                  "type": "string"
                },
                "description": "Declares the type of each variable an expression compares — string, number or boolean — keyed by its dotted path. Declare anything numeric, or \"18\" \u003e \"9\" is false.",
                "type": "object"
              }
            },
            "type": "object"
          },
          "group_id": {
            "description": "The agent group this agent belongs to, if any. Its defaults fill in every inheritable field not named in `overrides`.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "keypad_timeout_seconds": {
            "description": "How long a `keypad` menu waits for a keypress before prompting again. 0 means the default of 7 seconds.",
            "type": "integer"
          },
          "knowledge_base_id": {
            "description": "Grounds answers in this knowledge base.",
            "type": "string"
          },
          "language_detection": {
            "description": "Whether the agent moves into another of its languages when the caller speaks it. Meaningless with one language.",
            "enum": [
              "off",
              "follow"
            ],
            "type": "string"
          },
          "languages": {
            "description": "Every language the agent speaks, the one calls open in first. Each entry carries that language's prompt, voice, vendors and greeting; there is no prompt, language, voice or greeting field on the agent itself.",
            "items": {
              "properties": {
                "code": {
                  "description": "A BCP-47 tag, or a name — hi, hi-IN, hi_IN and Hindi all normalize to hi-IN. One entry per language.",
                  "type": "string"
                },
                "greeting": {
                  "description": "Spoken when a call opens in this language, exactly as written — write it in the language.",
                  "type": "string"
                },
                "silence_prompt": {
                  "description": "What the agent asks a caller who has gone quiet, in this language — \"Are you still there?\". Empty uses a built-in line where one exists, and otherwise asks the model for one short sentence.",
                  "type": "string"
                },
                "stt_provider": {
                  "description": "The recognizer to try first for this language. Read for the language a call opens in. Must be one this deployment configured.",
                  "type": "string"
                },
                "system_prompt": {
                  "description": "What the model is told while the call is in this language. This is the only place an agent's instructions live. The first entry's is the agent's prompt; empty on a later entry derives one from it plus an instruction to speak this language.",
                  "type": "string"
                },
                "tts_provider": {
                  "description": "The synthesizer to try first for this language. Must be one this deployment configured.",
                  "type": "string"
                },
                "voice_id": {
                  "description": "A voice at the deployment's first-choice synthesizer. Required on every entry but the first, which may leave it empty and take the deployment's default.",
                  "type": "string"
                },
                "voices": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "description": "Voice per synthesizer, keyed by provider name — {\"elevenlabs\": \"pNInz…\", \"deepgram-aura\": \"aura-2-thalia-en\"}. Voice ids are not portable between vendors, so without an entry a fallback synthesizer uses its own default voice.",
                  "type": "object"
                }
              },
              "required": [
                "code"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "max_no_input": {
            "description": "Rounds of silence a `keypad` menu allows before saying goodbye and hanging up. 0 means the default of 2 — one reminder, then goodbye.",
            "type": "integer"
          },
          "max_tokens": {
            "description": "Reply length cap: 1–4000, or 0 for the deployment's default.",
            "type": "integer"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "mode": {
            "description": "How the agent takes input. `conversation` listens and answers with a model. `keypad` answers only configured keypresses, with no recognizer and no model. `normal` reads the greeting and puts the caller through to transfer_number, with no AI at all. `webrtc` speaks to nobody: it introduces two browsers to each other and the audio runs directly between them, so a call on it is recorded or transcribed only when record_calls or transcribe_calls asks the two clients for a copy of what they are saying. Defaults to conversation.",
            "enum": [
              "conversation",
              "keypad",
              "normal",
              "webrtc"
            ],
            "type": "string"
          },
          "model": {
            "description": "The LLM to use.",
            "type": "string"
          },
          "models": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Model per language provider, keyed by provider name — {\"anthropic\": \"claude-opus-5\", \"gemini\": \"gemini-2.0-flash\"}. Overrides model above for that provider.",
            "type": "object"
          },
          "name": {
            "description": "A human label.",
            "type": "string"
          },
          "no_input": {
            "description": "Spoken by a `keypad` agent when the caller presses nothing, before the menu is read again.",
            "type": "string"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "overrides": {
            "description": "The inheritable fields whose value on this agent wins over its group's. An explicit list rather than \"a non-zero value means the operator chose it\", because for several of these the zero value is a real answer: an agent that deliberately does not record and one nobody has ever asked look identical in storage, and a group that records must not be able to tell them apart and switch recording on for both.",
            "items": {
              "enum": [
                "model",
                "models",
                "temperature",
                "max_tokens",
                "speed",
                "pitch",
                "autonomy",
                "capabilities",
                "knowledge_base_ids",
                "tools",
                "record_calls",
                "transcribe_calls",
                "transfer_number",
                "carrier",
                "filler_phrase",
                "webhook_url"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "pitch": {
            "description": "Pitch multiplier: 0.25–4, or 0 for the deployment's own.",
            "type": "number"
          },
          "post_call": {
            "description": "What is worked out from the transcript once the call ends. Absent means a summary and nothing else — the default is on. Replaces the whole block when sent.",
            "properties": {
              "extraction": {
                "description": "Structured fields to fill from the transcript, each answered by the model and written onto the call under its name. At most 40.",
                "items": {
                  "properties": {
                    "description": {
                      "description": "What to look for — the whole of what the model is told besides the transcript. Required.",
                      "type": "string"
                    },
                    "name": {
                      "description": "The key the value is stored under. Letters, digits and underscores; stable once live.",
                      "type": "string"
                    },
                    "options": {
                      "description": "The allowed values of an enum field. Add an \"unclear\" option rather than relying on the field being left empty.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "type": {
                      "description": "string (the default), number, boolean or enum.",
                      "enum": [
                        "string",
                        "number",
                        "boolean",
                        "enum"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "description"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "model": {
                "description": "Overrides the model the post-call pass runs on. Empty uses the deployment's post-call default.",
                "type": "string"
              },
              "summary": {
                "description": "Write a short neutral summary onto the call record. On unless this block is sent with it off.",
                "type": "boolean"
              }
            },
            "type": "object"
          },
          "record_calls": {
            "description": "Whether this agent's calls are recorded to audio.",
            "type": "boolean"
          },
          "ring_seconds": {
            "description": "How long a call rings before nobody has answered. 0 means the default of 30 seconds. Read by the two shapes that ring rather than answer: a `webrtc` agent, and a `simple` one taking an inbound call.",
            "type": "integer"
          },
          "speed": {
            "description": "Speech rate multiplier: 0.25–4, or 0 for the deployment's own. A multiplier on whichever voice is speaking, in every language.",
            "type": "number"
          },
          "temperature": {
            "description": "Sampling temperature (0–2).",
            "type": "number"
          },
          "tool_configs": {
            "description": "The tools this agent carries itself, as opposed to `tools`, which names functions the deployment registered. Replaces the whole list.",
            "items": {
              "properties": {
                "calendar": {
                  "description": "For `calendar_availability` and `book_appointment`.",
                  "properties": {
                    "api_key": {
                      "description": "The calendar account's API key. Write it as {\"value\": \"…\"}; it reads back as {\"hint\": \"••••1234\"}. Leaving it out on an update keeps what is stored.",
                      "properties": {
                        "hint": {
                          "description": "The last few characters, on the way out only.",
                          "type": "string"
                        },
                        "value": {
                          "description": "The credential, on the way in only.",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "duration_minutes": {
                      "description": "Overrides the event's own length. Only meaningful on an event that offers a choice.",
                      "type": "integer"
                    },
                    "event_type_id": {
                      "description": "Which bookable event on that calendar. It carries the length and the availability schedule, so it is most of the configuration.",
                      "type": "integer"
                    },
                    "event_type_slug": {
                      "description": "Carried for display.",
                      "type": "string"
                    },
                    "event_type_title": {
                      "description": "Carried for display.",
                      "type": "string"
                    },
                    "provider": {
                      "description": "Only `cal.com` today.",
                      "type": "string"
                    },
                    "timezone": {
                      "description": "An IANA zone — the caller's, not the business's. Empty uses the agent's.",
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "description": {
                  "description": "The whole of what the model is told about when to use this. Say when to call it, what to do first, and when not to.",
                  "type": "string"
                },
                "kind": {
                  "description": "Which shape this tool has. It decides which configuration block below applies.",
                  "enum": [
                    "calendar_availability",
                    "book_appointment",
                    "transfer_call",
                    "custom_function"
                  ],
                  "type": "string"
                },
                "name": {
                  "description": "What the model calls. Letters, digits, _ and - only. Cannot be `end_call` or `search_knowledge_base`, which the runtime answers itself, or the name of a registered function.",
                  "type": "string"
                },
                "pre_tool_message": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "description": "What the agent says while the tool runs, keyed by language code. Falls back to the agent's filler_phrase.",
                  "type": "object"
                },
                "request": {
                  "description": "For `custom_function`.",
                  "properties": {
                    "body_template": {
                      "description": "A raw body with {{parameter}} placeholders, for an endpoint whose body is mostly fixed. Empty sends the body parameters as a JSON object.",
                      "type": "string"
                    },
                    "headers": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Sent as-is and readable in the portal. Anything secret belongs below.",
                      "type": "object"
                    },
                    "method": {
                      "description": "GET, POST, PUT, PATCH or DELETE. Defaults to POST.",
                      "type": "string"
                    },
                    "parameters": {
                      "description": "What the model is asked to supply. The schema is closed: an argument nobody listed is refused rather than forwarded.",
                      "items": {
                        "properties": {
                          "description": {
                            "description": "What the model should put here.",
                            "type": "string"
                          },
                          "enum": {
                            "description": "Restricts a string to a fixed set — the most effective way to stop a model inventing a value.",
                            "items": {
                              "type": "string"
                            },
                            "type": "array"
                          },
                          "in": {
                            "description": "Where the value goes. Defaults to the body.",
                            "enum": [
                              "body",
                              "query",
                              "path"
                            ],
                            "type": "string"
                          },
                          "name": {
                            "description": "Letters, digits, _ and - only.",
                            "type": "string"
                          },
                          "required": {
                            "description": "Whether the model must supply it.",
                            "type": "boolean"
                          },
                          "type": {
                            "enum": [
                              "string",
                              "number",
                              "integer",
                              "boolean"
                            ],
                            "type": "string"
                          }
                        },
                        "required": [
                          "name"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "secret_header_names": {
                      "description": "Read-only: which credentials this tool carries.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "secret_header_values": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Sealed on write and never read back. Present replaces them whole; absent leaves them alone.",
                      "type": "object"
                    },
                    "timeout_seconds": {
                      "description": "1–30, defaulting to 8. A caller is waiting on it.",
                      "type": "integer"
                    },
                    "url": {
                      "description": "https, or http on an internal endpoint. May carry {{parameter}} placeholders, filled from path arguments and percent-escaped.",
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "transfer": {
                  "description": "For `transfer_call`.",
                  "properties": {
                    "destination": {
                      "description": "E.164, or a `sip:` URI. Fixed here and never chosen by the model.",
                      "type": "string"
                    },
                    "webhook_url": {
                      "description": "Posted the transfer's details before the call moves. Waited on for three seconds at most.",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              },
              "required": [
                "kind",
                "name",
                "description"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "tools": {
            "description": "Function names this agent may call.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "transcribe_calls": {
            "description": "Transcribe a `webrtc` agent's calls — the two people on them. Off by default and meaningless on every other mode, which is transcribed anyway because the recognizer is how the agent hears the caller at all. The audio of a bridged call never reaches this deployment, so this asks both clients to send a copy of their own microphone up the realtime socket; a client that sends nothing produces nothing.",
            "type": "boolean"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          },
          "webhook_url": {
            "description": "Overrides tenant webhooks for this agent's events.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "languages"
        ],
        "type": "object"
      },
      "AgentGroup": {
        "properties": {
          "color": {
            "description": "Dots this group in a list. Omit for none.",
            "enum": [
              "slate",
              "red",
              "orange",
              "amber",
              "green",
              "teal",
              "blue",
              "indigo",
              "violet",
              "pink"
            ],
            "type": "string"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "defaults": {
            "$ref": "#/components/schemas/GroupDefaults"
          },
          "description": {
            "description": "What this group is for.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label — what these agents have in common.",
            "type": "string"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "type": "object"
      },
      "AgentGroupInput": {
        "properties": {
          "color": {
            "description": "Dots this group in a list. Omit for none.",
            "enum": [
              "slate",
              "red",
              "orange",
              "amber",
              "green",
              "teal",
              "blue",
              "indigo",
              "violet",
              "pink"
            ],
            "type": "string"
          },
          "defaults": {
            "$ref": "#/components/schemas/GroupDefaults"
          },
          "description": {
            "description": "What this group is for.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label. Required on create.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AgentGroupMember": {
        "properties": {
          "agent_id": {
            "description": "The member's ID.",
            "type": "string"
          },
          "differences": {
            "description": "Fields where the agent's value and the group's actually differ, so adopting the group's defaults would change this agent. A field the agent has claimed but holds the group's value for is not listed.",
            "items": {
              "properties": {
                "agent": {
                  "description": "What this agent answers with today."
                },
                "field": {
                  "description": "Which field.",
                  "type": "string"
                },
                "group": {
                  "description": "What it would answer with if the override were dropped."
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "name": {
            "description": "Its name.",
            "type": "string"
          },
          "overrides": {
            "description": "The fields this agent has claimed as its own.",
            "items": {
              "enum": [
                "model",
                "models",
                "temperature",
                "max_tokens",
                "speed",
                "pitch",
                "autonomy",
                "capabilities",
                "knowledge_base_ids",
                "tools",
                "record_calls",
                "transcribe_calls",
                "transfer_number",
                "carrier",
                "filler_phrase",
                "webhook_url"
              ],
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "agent_id",
          "name"
        ],
        "type": "object"
      },
      "AgentGroupMembers": {
        "properties": {
          "differing": {
            "description": "How many members differ from the group's defaults — the number to lead a group page with.",
            "type": "integer"
          },
          "group_id": {
            "description": "The group these agents are in.",
            "type": "string"
          },
          "members": {
            "items": {
              "$ref": "#/components/schemas/AgentGroupMember"
            },
            "type": "array"
          },
          "object": {
            "description": "Always `agent_group_members`.",
            "type": "string"
          }
        },
        "required": [
          "object",
          "group_id",
          "members",
          "differing"
        ],
        "type": "object"
      },
      "AgentInput": {
        "properties": {
          "autonomy": {
            "description": "Who holds a `conversation` agent's end of the call. `agentic` is a model that searches the agent's knowledge bases before it answers and calls the tools in `tools`. `simple` is not a model at all: a person takes the call in their browser, and no speech recognizer, model or synthesizer is opened for it — the audio passes between the phone and the operator's WebSocket and nothing on this platform listens to it. An inbound number pointed at a simple agent rings rather than answers, and is picked up with `POST /v1/calls/{id}/claim`, exactly as a `webrtc` call is. `custom` is a model doing exactly what `capabilities` switches on, for the lines between the two — read the documents but never write to a system, call the functions but answer from what it was told, never offer a transfer because there is nobody behind it. Meaningless on the other three modes, which never reach a model. Defaults to agentic. Note that `simple` changed meaning: it used to mean a model that talks without retrieval or functions, which is `custom` with `search_knowledge` and `call_functions` off. Agents stored under the old meaning are read as exactly that and are returned with `autonomy` of `custom`.",
            "enum": [
              "agentic",
              "simple",
              "custom"
            ],
            "type": "string"
          },
          "capabilities": {
            "description": "What the agent may do besides talk, switch by switch. Read only when `autonomy` is `custom` — `agentic` resolves from its own name and `simple` has no model on the call to grant anything to. It is stored and returned on every agent whatever the autonomy is, so trying another one and coming back does not lose what you set. Sending it replaces the whole block. Omitted on a `custom` agent it means call control and nothing else — talking, plus hanging up and reaching a person — because four absent booleans are a block nobody wrote, not a decision to build a line that cannot even end its own call.",
            "properties": {
              "call_functions": {
                "description": "Call the functions in `tools`. A master switch over that list, not a second copy of it — which functions the agent has is still `tools`.",
                "type": "boolean"
              },
              "end_call": {
                "description": "Offer `end_call`, the other tool the runtime executes itself.",
                "type": "boolean"
              },
              "max_tool_rounds": {
                "description": "How many times one turn may call a tool and generate again. 1–10, or 0 for the default of 3. Every round is a model round trip with the caller listening to the filler phrase.",
                "type": "integer"
              },
              "retrieval_timeout_ms": {
                "description": "How long a knowledge lookup may take inside the turn. 50–2000, or 0 for the deployment's setting and then 250ms. Past it the turn answers ungrounded rather than leaving the line silent.",
                "type": "integer"
              },
              "search_knowledge": {
                "description": "Search the bases in `knowledge_base_ids` during a turn, and offer `search_knowledge_base`. Off, no lookup happens and none can time out.",
                "type": "boolean"
              },
              "transfer": {
                "description": "Offer `transfer_call`. Separate from `call_functions` because the runtime executes it against the call itself, not against one of your systems.",
                "type": "boolean"
              }
            },
            "type": "object"
          },
          "conversation": {
            "description": "How the agent listens and when it gives up. Replaces the whole block when sent; every 0 inside it means the default, which is what a good phone agent does unasked.",
            "properties": {
              "interrupt_phrases": {
                "description": "Extra words or short phrases that interrupt the agent on their own, on top of the built-in list. Matched against the whole utterance.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "interrupt_words": {
                "description": "How many words the caller has to say before the agent stops talking. Below it, a \"hmm\" or an \"okay\" over the agent is the caller listening, not taking the floor. A stop word (wait, no, ruko) interrupts on its own, and so does a caller who keeps talking. 1–10; 0 means 2.",
                "type": "integer"
              },
              "max_call_seconds": {
                "description": "Ends the call, after a goodbye, once it has run this long. 30–7200; 0 leaves it to the carrier's own limit.",
                "type": "integer"
              },
              "silence_hangup_seconds": {
                "description": "How long the line may be quiet before the agent says goodbye and hangs up, measured from the same moment. 5–300 and longer than silence_prompt_seconds; 0 means 20; -1 never hangs up for silence.",
                "type": "integer"
              },
              "silence_prompt_seconds": {
                "description": "How long the line may be quiet before the agent asks whether the caller is still there. 3–60; 0 means 8; -1 never asks.",
                "type": "integer"
              },
              "voicemail": {
                "description": "What an outbound call does when the carrier reports an answering machine. continue (the default) carries on and the record says voicemail; hangup ends the call at once and says nothing; message says voicemail_message and hangs up.",
                "enum": [
                  "continue",
                  "hangup",
                  "message"
                ],
                "type": "string"
              },
              "voicemail_message": {
                "description": "What \"message\" leaves on the machine. Supports {{variables}}. At most 2000 characters.",
                "type": "string"
              }
            },
            "type": "object"
          },
          "description": {
            "description": "What this agent is for.",
            "type": "string"
          },
          "filler_phrase": {
            "description": "Spoken while a tool runs.",
            "type": "string"
          },
          "flow": {
            "description": "A conversation flow: the nodes a call moves between and the edges that move it. The agent's prompt — the opening language's system_prompt — is the global instruction every node is read under. Send {} to take the flow away. Validated in full; every problem is reported at once under flow.\u003cpath\u003e.",
            "properties": {
              "nodes": {
                "items": {
                  "properties": {
                    "description": {
                      "description": "What the node is for. Read by the router as the objective when the node has no prompt.",
                      "type": "string"
                    },
                    "edges": {
                      "items": {
                        "properties": {
                          "capture": {
                            "additionalProperties": {
                              "type": "string"
                            },
                            "description": "Values the router extracts from the caller's words when this intent edge fires, keyed by name with a JSON type — {\"amount\": \"number\"}. Stored as call variables; every one is required for the edge to fire.",
                            "type": "object"
                          },
                          "event": {
                            "description": "The injected event an event edge waits for.",
                            "type": "string"
                          },
                          "expression": {
                            "description": "What an expression edge evaluates. Flat: one logic word over a list of conditions.",
                            "properties": {
                              "conditions": {
                                "items": {
                                  "properties": {
                                    "operator": {
                                      "description": "How to compare.",
                                      "enum": [
                                        "eq",
                                        "neq",
                                        "gt",
                                        "gte",
                                        "lt",
                                        "lte",
                                        "in",
                                        "not_in",
                                        "contains",
                                        "exists",
                                        "not_exists"
                                      ],
                                      "type": "string"
                                    },
                                    "value": {
                                      "description": "What to compare against. Unused by exists and not_exists."
                                    },
                                    "variable": {
                                      "description": "A dotted path into the call's variables: amount, caller.city, _node_turns.",
                                      "type": "string"
                                    }
                                  },
                                  "required": [
                                    "variable",
                                    "operator"
                                  ],
                                  "type": "object"
                                },
                                "type": "array"
                              },
                              "logic": {
                                "description": "and (the default) or or.",
                                "enum": [
                                  "and",
                                  "or"
                                ],
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "priority": {
                            "description": "Orders edges of the same type; lower fires first.",
                            "type": "integer"
                          },
                          "to": {
                            "description": "The id of the node this edge leads to.",
                            "type": "string"
                          },
                          "type": {
                            "description": "How the edge decides: intent (the model, from what the caller said — the default), expression (evaluated first, free), always (the catch-all), event (only when an outside system injects the named event).",
                            "enum": [
                              "intent",
                              "expression",
                              "always",
                              "event"
                            ],
                            "type": "string"
                          },
                          "when": {
                            "description": "The condition in a sentence. What the router reads on an intent edge; a label on the others.",
                            "type": "string"
                          }
                        },
                        "required": [
                          "to"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "examples": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "One example reply per language code, appended to the prompt.",
                      "type": "object"
                    },
                    "id": {
                      "description": "Names the node. Unique.",
                      "type": "string"
                    },
                    "knowledge_base_ids": {
                      "description": "The bases this node grounds against, overriding the agent's. Empty means no grounding here.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "prompt": {
                      "description": "This node's instructions, appended to the agent's system prompt while the call is here.",
                      "type": "string"
                    },
                    "repeat_after_silence_seconds": {
                      "description": "Re-prompt a caller who has said nothing for this long while the call sits here. 0 never re-prompts from this node.",
                      "type": "integer"
                    },
                    "say": {
                      "description": "What a static node says, exactly as written, with {{variables}} filled.",
                      "type": "string"
                    },
                    "say_in": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "The same line per language code, for an agent that speaks more than one.",
                      "type": "object"
                    },
                    "tool": {
                      "description": "Forces the model to call this tool while the call is on this node, until it has run once on this visit.",
                      "type": "string"
                    },
                    "tools": {
                      "description": "Restricts the tools the model sees here. Absent means all of the agent's; empty means none.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "type": {
                      "description": "llm (the model answers under the agent's prompt plus this node's — the default), static (says a fixed line, no model) or router (never speaks; moves on in the same turn).",
                      "enum": [
                        "llm",
                        "static",
                        "router"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "id"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "routing": {
                "description": "Tunes the model that chooses between a node's intent edges.",
                "properties": {
                  "instructions": {
                    "description": "Replaces the built-in routing instructions. Rarely needed.",
                    "type": "string"
                  },
                  "model": {
                    "description": "Overrides the agent's model for routing. A small fast one is right: the decision is one function call from a short prompt.",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "start": {
                "description": "The id of the node a call opens on.",
                "type": "string"
              },
              "variables": {
                "additionalProperties": {
                  "type": "string"
                },
                "description": "Declares the type of each variable an expression compares — string, number or boolean — keyed by its dotted path. Declare anything numeric, or \"18\" \u003e \"9\" is false.",
                "type": "object"
              }
            },
            "type": "object"
          },
          "group_id": {
            "description": "Files this agent into an agent group; empty removes it from one. Must name a group you own. Setting it where there was none seeds `overrides` with everything this agent already has a value for, so joining a group changes nothing about how it answers — see the agent groups section of the API reference.",
            "type": "string"
          },
          "keypad_timeout_seconds": {
            "description": "2–60, or 0 for the default of 7 seconds. Only a `keypad` agent waits.",
            "type": "integer"
          },
          "knowledge_base_id": {
            "description": "Must name a knowledge base you own.",
            "type": "string"
          },
          "language_detection": {
            "description": "follow (the default once the agent has more than one language) or off: whether the agent moves into another of its languages when the caller speaks it.",
            "enum": [
              "off",
              "follow"
            ],
            "type": "string"
          },
          "languages": {
            "description": "Every language the agent speaks, the one calls open in first. Replaces the whole list, and the order is the meaning. The first entry carries the agent's prompt, and is required on create for any agent a model answers. Every entry after the first needs a voice; a vendor named on any entry must be one this deployment configured.",
            "items": {
              "properties": {
                "code": {
                  "description": "A BCP-47 tag, or a name — hi, hi-IN, hi_IN and Hindi all normalize to hi-IN. One entry per language.",
                  "type": "string"
                },
                "greeting": {
                  "description": "Spoken when a call opens in this language, exactly as written — write it in the language.",
                  "type": "string"
                },
                "silence_prompt": {
                  "description": "What the agent asks a caller who has gone quiet, in this language — \"Are you still there?\". Empty uses a built-in line where one exists, and otherwise asks the model for one short sentence.",
                  "type": "string"
                },
                "stt_provider": {
                  "description": "The recognizer to try first for this language. Read for the language a call opens in. Must be one this deployment configured.",
                  "type": "string"
                },
                "system_prompt": {
                  "description": "What the model is told while the call is in this language. This is the only place an agent's instructions live. The first entry's is the agent's prompt; empty on a later entry derives one from it plus an instruction to speak this language.",
                  "type": "string"
                },
                "tts_provider": {
                  "description": "The synthesizer to try first for this language. Must be one this deployment configured.",
                  "type": "string"
                },
                "voice_id": {
                  "description": "A voice at the deployment's first-choice synthesizer. Required on every entry but the first, which may leave it empty and take the deployment's default.",
                  "type": "string"
                },
                "voices": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "description": "Voice per synthesizer, keyed by provider name — {\"elevenlabs\": \"pNInz…\", \"deepgram-aura\": \"aura-2-thalia-en\"}. Voice ids are not portable between vendors, so without an entry a fallback synthesizer uses its own default voice.",
                  "type": "object"
                }
              },
              "required": [
                "code"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "max_no_input": {
            "description": "1–10, or 0 for the default of 2. Only a `keypad` agent counts silence.",
            "type": "integer"
          },
          "max_tokens": {
            "description": "1–4000, or 0 for the deployment's default. Defaults to 512.\n\nThe ceiling is a product decision rather than a model limit: this is a phone call, and a long reply is a reply the caller talks over. Zero is what an agent with no model carries — a line a person answers has no reply to cap.",
            "type": "integer"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "mode": {
            "description": "How the agent takes input. `conversation` listens and answers with a model. `keypad` answers only configured keypresses, with no recognizer and no model. `normal` reads the greeting and puts the caller through to transfer_number, with no AI at all. `webrtc` speaks to nobody: it introduces two browsers to each other and the audio runs directly between them, so a call on it is recorded or transcribed only when record_calls or transcribe_calls asks the two clients for a copy of what they are saying. Defaults to conversation.",
            "enum": [
              "conversation",
              "keypad",
              "normal",
              "webrtc"
            ],
            "type": "string"
          },
          "model": {
            "description": "Defaults to claude-sonnet-5.",
            "type": "string"
          },
          "models": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Model per language provider, keyed by provider name — {\"anthropic\": \"claude-opus-5\", \"gemini\": \"gemini-2.0-flash\"}. Overrides model above for that provider. Replaces the whole map.",
            "type": "object"
          },
          "name": {
            "description": "A human label. Required on create.",
            "type": "string"
          },
          "no_input": {
            "description": "At most 1000 characters — it is spoken aloud. Only a `keypad` agent says it.",
            "type": "string"
          },
          "overrides": {
            "description": "Replaces the whole list. Sending it alongside a `group_id` that was not previously set suppresses the seeding described above, which is what a bulk migration wants. Every name must be a field a group can set; anything else is refused rather than dropped, because a misspelt override is an agent that silently takes the group's value for the one field its operator meant to keep.",
            "items": {
              "enum": [
                "model",
                "models",
                "temperature",
                "max_tokens",
                "speed",
                "pitch",
                "autonomy",
                "capabilities",
                "knowledge_base_ids",
                "tools",
                "record_calls",
                "transcribe_calls",
                "transfer_number",
                "carrier",
                "filler_phrase",
                "webhook_url"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "pitch": {
            "description": "0.25–4, or 0 for the deployment's own. Defaults to 1.",
            "type": "number"
          },
          "post_call": {
            "description": "What is worked out from the transcript once the call ends. Absent means a summary and nothing else — the default is on. Replaces the whole block when sent.",
            "properties": {
              "extraction": {
                "description": "Structured fields to fill from the transcript, each answered by the model and written onto the call under its name. At most 40.",
                "items": {
                  "properties": {
                    "description": {
                      "description": "What to look for — the whole of what the model is told besides the transcript. Required.",
                      "type": "string"
                    },
                    "name": {
                      "description": "The key the value is stored under. Letters, digits and underscores; stable once live.",
                      "type": "string"
                    },
                    "options": {
                      "description": "The allowed values of an enum field. Add an \"unclear\" option rather than relying on the field being left empty.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "type": {
                      "description": "string (the default), number, boolean or enum.",
                      "enum": [
                        "string",
                        "number",
                        "boolean",
                        "enum"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "description"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "model": {
                "description": "Overrides the model the post-call pass runs on. Empty uses the deployment's post-call default.",
                "type": "string"
              },
              "summary": {
                "description": "Write a short neutral summary onto the call record. On unless this block is sent with it off.",
                "type": "boolean"
              }
            },
            "type": "object"
          },
          "record_calls": {
            "description": "Record this agent's calls. Off by default, and needs recording storage configured on the deployment.",
            "type": "boolean"
          },
          "ring_seconds": {
            "description": "1–120, or 0 for the default of 30 seconds. Read by a `webrtc` agent and by a `simple` one taking an inbound call.",
            "type": "integer"
          },
          "speed": {
            "description": "0.25–4, or 0 for the deployment's own. Defaults to 1.",
            "type": "number"
          },
          "temperature": {
            "description": "0–2. Defaults to 0.6.",
            "type": "number"
          },
          "tool_configs": {
            "description": "The tools this agent carries itself, as opposed to `tools`, which names functions the deployment registered. Replaces the whole list.",
            "items": {
              "properties": {
                "calendar": {
                  "description": "For `calendar_availability` and `book_appointment`.",
                  "properties": {
                    "api_key": {
                      "description": "The calendar account's API key. Write it as {\"value\": \"…\"}; it reads back as {\"hint\": \"••••1234\"}. Leaving it out on an update keeps what is stored.",
                      "properties": {
                        "hint": {
                          "description": "The last few characters, on the way out only.",
                          "type": "string"
                        },
                        "value": {
                          "description": "The credential, on the way in only.",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "duration_minutes": {
                      "description": "Overrides the event's own length. Only meaningful on an event that offers a choice.",
                      "type": "integer"
                    },
                    "event_type_id": {
                      "description": "Which bookable event on that calendar. It carries the length and the availability schedule, so it is most of the configuration.",
                      "type": "integer"
                    },
                    "event_type_slug": {
                      "description": "Carried for display.",
                      "type": "string"
                    },
                    "event_type_title": {
                      "description": "Carried for display.",
                      "type": "string"
                    },
                    "provider": {
                      "description": "Only `cal.com` today.",
                      "type": "string"
                    },
                    "timezone": {
                      "description": "An IANA zone — the caller's, not the business's. Empty uses the agent's.",
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "description": {
                  "description": "The whole of what the model is told about when to use this. Say when to call it, what to do first, and when not to.",
                  "type": "string"
                },
                "kind": {
                  "description": "Which shape this tool has. It decides which configuration block below applies.",
                  "enum": [
                    "calendar_availability",
                    "book_appointment",
                    "transfer_call",
                    "custom_function"
                  ],
                  "type": "string"
                },
                "name": {
                  "description": "What the model calls. Letters, digits, _ and - only. Cannot be `end_call` or `search_knowledge_base`, which the runtime answers itself, or the name of a registered function.",
                  "type": "string"
                },
                "pre_tool_message": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "description": "What the agent says while the tool runs, keyed by language code. Falls back to the agent's filler_phrase.",
                  "type": "object"
                },
                "request": {
                  "description": "For `custom_function`.",
                  "properties": {
                    "body_template": {
                      "description": "A raw body with {{parameter}} placeholders, for an endpoint whose body is mostly fixed. Empty sends the body parameters as a JSON object.",
                      "type": "string"
                    },
                    "headers": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Sent as-is and readable in the portal. Anything secret belongs below.",
                      "type": "object"
                    },
                    "method": {
                      "description": "GET, POST, PUT, PATCH or DELETE. Defaults to POST.",
                      "type": "string"
                    },
                    "parameters": {
                      "description": "What the model is asked to supply. The schema is closed: an argument nobody listed is refused rather than forwarded.",
                      "items": {
                        "properties": {
                          "description": {
                            "description": "What the model should put here.",
                            "type": "string"
                          },
                          "enum": {
                            "description": "Restricts a string to a fixed set — the most effective way to stop a model inventing a value.",
                            "items": {
                              "type": "string"
                            },
                            "type": "array"
                          },
                          "in": {
                            "description": "Where the value goes. Defaults to the body.",
                            "enum": [
                              "body",
                              "query",
                              "path"
                            ],
                            "type": "string"
                          },
                          "name": {
                            "description": "Letters, digits, _ and - only.",
                            "type": "string"
                          },
                          "required": {
                            "description": "Whether the model must supply it.",
                            "type": "boolean"
                          },
                          "type": {
                            "enum": [
                              "string",
                              "number",
                              "integer",
                              "boolean"
                            ],
                            "type": "string"
                          }
                        },
                        "required": [
                          "name"
                        ],
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "secret_header_names": {
                      "description": "Read-only: which credentials this tool carries.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "secret_header_values": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Sealed on write and never read back. Present replaces them whole; absent leaves them alone.",
                      "type": "object"
                    },
                    "timeout_seconds": {
                      "description": "1–30, defaulting to 8. A caller is waiting on it.",
                      "type": "integer"
                    },
                    "url": {
                      "description": "https, or http on an internal endpoint. May carry {{parameter}} placeholders, filled from path arguments and percent-escaped.",
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "transfer": {
                  "description": "For `transfer_call`.",
                  "properties": {
                    "destination": {
                      "description": "E.164, or a `sip:` URI. Fixed here and never chosen by the model.",
                      "type": "string"
                    },
                    "webhook_url": {
                      "description": "Posted the transfer's details before the call moves. Waited on for three seconds at most.",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              },
              "required": [
                "kind",
                "name",
                "description"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "tools": {
            "description": "Must name registered functions.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "transcribe_calls": {
            "description": "Transcribe a `webrtc` agent's calls — the two people on them. Off by default and meaningless on every other mode, which is transcribed anyway because the recognizer is how the agent hears the caller at all. The audio of a bridged call never reaches this deployment, so this asks both clients to send a copy of their own microphone up the realtime socket; a client that sends nothing produces nothing.",
            "type": "boolean"
          },
          "webhook_url": {
            "description": "Must be https.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AnalyticsOverview": {
        "properties": {
          "active_calls": {
            "description": "Calls in progress.",
            "type": "integer"
          },
          "calls": {
            "description": "Calls placed or received.",
            "type": "integer"
          },
          "completed_calls": {
            "description": "Calls that finished.",
            "type": "integer"
          },
          "cost": {
            "$ref": "#/components/schemas/Cost"
          },
          "cost_per_call": {
            "description": "Average cost per call.",
            "type": "number"
          },
          "csat": {
            "description": "Satisfaction summary.",
            "type": "object"
          },
          "latency": {
            "description": "Percentiles per pipeline stage.",
            "type": "object"
          },
          "total_minutes": {
            "description": "Talk time.",
            "type": "number"
          },
          "turns": {
            "description": "Conversational turns.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "CSATAccepted": {
        "properties": {
          "call_id": {
            "description": "The call that was rated.",
            "type": "string"
          },
          "object": {
            "description": "Always `csat`.",
            "type": "string"
          },
          "score": {
            "description": "The score recorded.",
            "type": "integer"
          }
        },
        "required": [
          "object",
          "call_id",
          "score"
        ],
        "type": "object"
      },
      "CSATInput": {
        "properties": {
          "call_id": {
            "description": "The call being rated. It must belong to your account.",
            "type": "string"
          },
          "comment": {
            "description": "What the caller said, in their words. Kept and shown verbatim, trimmed to 500 characters.",
            "type": "string"
          },
          "score": {
            "description": "1 is worst, 5 is best. Scores of 4 and 5 count as promoters.",
            "maximum": 5,
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "call_id",
          "score"
        ],
        "type": "object"
      },
      "Call": {
        "properties": {
          "agent_id": {
            "description": "The agent handling the call.",
            "type": "string"
          },
          "analysed_at": {
            "description": "When `summary` and `extracted` were written. Absent until they have been.",
            "format": "date-time",
            "type": "string"
          },
          "analysis_model": {
            "description": "The model the post-call pass ran on.",
            "type": "string"
          },
          "analysis_skipped": {
            "description": "Why the post-call pass finished without writing a summary — the agent asks for none, the model failed, nothing was settled. Absent while the pass may still run and on every call it wrote something.",
            "type": "string"
          },
          "claimed_by": {
            "description": "Whoever answered a ringing call, in your own vocabulary. Set by the claim route; `webrtc` calls only.",
            "type": "string"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "direction": {
            "description": "How the call was established.",
            "enum": [
              "inbound",
              "outbound",
              "web"
            ],
            "type": "string"
          },
          "duration_seconds": {
            "description": "How long it lasted.",
            "type": "number"
          },
          "end_reason": {
            "description": "Why it ended.",
            "type": "string"
          },
          "ended_at": {
            "description": "When it finished.",
            "format": "date-time",
            "type": "string"
          },
          "extracted": {
            "additionalProperties": true,
            "description": "The agent's `post_call.extraction` fields the transcript settled, by name, each in its declared type. A field the transcript did not answer is absent rather than null.",
            "type": "object"
          },
          "from": {
            "description": "Caller number.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "language": {
            "description": "Conversation language.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "realtime_url": {
            "description": "WebSocket URL for a web call. Present only on create. On a `webrtc` agent the second person connects to the same URL with `role=responder`.",
            "type": "string"
          },
          "recording_url": {
            "description": "Stored audio, when recording is enabled.",
            "type": "string"
          },
          "rings_until": {
            "description": "When a `ringing` call stops ringing. Absent once it has been claimed or given up on.",
            "format": "date-time",
            "type": "string"
          },
          "source": {
            "description": "What placed the call. Read-only and derived from the endpoint and credential the request used, never from the request body, so nothing can file itself under the wrong one. Absent on calls placed before this was recorded, which is not the same as `api`.",
            "enum": [
              "app",
              "sdk",
              "api",
              "campaign",
              "inbound"
            ],
            "type": "string"
          },
          "source_client": {
            "description": "The exact client behind `source` — `sphoro-voice-node/1.0.0`, `console`, `twilio` — when one named itself. Free text: render it, do not branch on it.",
            "type": "string"
          },
          "started_at": {
            "description": "When it began.",
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "description": "Lifecycle state. `ringing` only ever appears on a `webrtc` agent's calls: the call exists and is waiting for a second person, and becomes `in_progress` when somebody claims it.",
            "enum": [
              "queued",
              "ringing",
              "in_progress",
              "completed",
              "failed"
            ],
            "type": "string"
          },
          "summary": {
            "description": "A short neutral paragraph of what happened on the call, written by the post-call pass a few seconds after `call.ended` and announced by `call.analysed`. Absent until then, on a call nobody spoke on, and when the agent's `post_call.summary` is off.",
            "type": "string"
          },
          "to": {
            "description": "Called number.",
            "type": "string"
          },
          "transcript": {
            "items": {
              "$ref": "#/components/schemas/TranscriptEntry"
            },
            "type": "array"
          },
          "turns": {
            "description": "Caller turns.",
            "type": "integer"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "agent_id",
          "status"
        ],
        "type": "object"
      },
      "CallEventInput": {
        "properties": {
          "event": {
            "description": "What happened outside the call — payment_received, otp_verified. Letters, digits, underscores and dots; at most 80 characters.",
            "type": "string"
          },
          "properties": {
            "additionalProperties": true,
            "description": "Travels with the event and becomes the call's variables whether or not the flow has an edge for it. At most 50 keys, 16KB.",
            "type": "object"
          }
        },
        "required": [
          "event"
        ],
        "type": "object"
      },
      "CallInput": {
        "properties": {
          "agent_id": {
            "description": "Which agent should handle the call.",
            "type": "string"
          },
          "direction": {
            "description": "Defaults to web when `to` is absent, outbound when present.",
            "enum": [
              "inbound",
              "outbound",
              "web"
            ],
            "type": "string"
          },
          "from": {
            "description": "Caller ID to present. Overrides the member's own number and the agent's rotation.",
            "type": "string"
          },
          "language": {
            "description": "Opens the call in one of the agent's languages. Defaults to the first. A language the agent does not speak is refused.",
            "type": "string"
          },
          "member_id": {
            "description": "The member placing the call. Sets the caller ID to the number assigned to them, and attributes the call to them so it appears in their own history.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "to": {
            "description": "Number to dial. Omit for a web call.",
            "type": "string"
          },
          "variables": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          }
        },
        "required": [
          "agent_id"
        ],
        "type": "object"
      },
      "CallSeat": {
        "properties": {
          "call_id": {
            "description": "The call this seat is on.",
            "type": "string"
          },
          "claimed_by": {
            "description": "Whoever answered, once somebody has.",
            "type": "string"
          },
          "expires_at": {
            "description": "When the credential stops opening the socket.",
            "format": "date-time",
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "realtime_url": {
            "description": "The socket, without the credential.",
            "type": "string"
          },
          "record": {
            "description": "Whether this call is being recorded. Say so to the person on this end before they speak: on a peer-to-peer call the kept copy is uplinked by their own browser, which makes this the only honest moment to mention it.",
            "type": "boolean"
          },
          "rings_until": {
            "description": "When it stops ringing, while it still is.",
            "format": "date-time",
            "type": "string"
          },
          "role": {
            "description": "The end that was taken.",
            "enum": [
              "caller",
              "responder"
            ],
            "type": "string"
          },
          "status": {
            "description": "The call's status after taking the seat — `in_progress` once the responder has, still `ringing` for the caller.",
            "type": "string"
          },
          "subprotocol": {
            "description": "The WebSocket subprotocol to request.",
            "type": "string"
          },
          "token": {
            "description": "The credential. It names this one call, carries `calls:write` alone, and is refused on every path but `GET /v1/realtime`.",
            "type": "string"
          },
          "transcribe": {
            "description": "Whether what is said on this call is being kept.",
            "type": "boolean"
          },
          "url": {
            "description": "The socket with the credential and the seat already on it. Hand this to the browser and nothing else.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CallSeatInput": {
        "properties": {
          "participant": {
            "description": "Who is taking it, in your own vocabulary — a user id, an email, a desk number. Stored verbatim as `claimed_by` and never interpreted. Required on the responder seat, because taking it answers the call and the record has to say who did; meaningless on the caller seat, which belongs to whoever placed the call.",
            "type": "string"
          },
          "role": {
            "description": "Which end is being taken. The responder makes the WebRTC offer.",
            "enum": [
              "caller",
              "responder"
            ],
            "type": "string"
          },
          "ttl_seconds": {
            "description": "How long this credential may be used to open the socket — not how long the call may last. Default 300, maximum 3600.",
            "type": "integer"
          }
        },
        "required": [
          "role"
        ],
        "type": "object"
      },
      "CallTokenInput": {
        "properties": {
          "member_id": {
            "description": "The same person in *this platform's* vocabulary — the member id the portal shows. A number that is somebody's direct line is assigned to a member id, so it is the only identifier a seat check can be made against; `participant` is your own label and is never matched against an assignment. Send it whenever any number on the account is a direct line, and send the same id you pass to `GET /calls/ringing?member_id=`. Omitted, the check falls back to `participant`, which only works for integrations that use our ids for both. It must come from your server, never from the page.",
            "type": "string"
          },
          "participant": {
            "description": "Who is holding this end of the call, in your own vocabulary — a user id, an email, a desk number. Stored verbatim and never interpreted. It identifies the seat: a call somebody else already holds is refused, and the holder may reconnect. Required.",
            "type": "string"
          },
          "ttl_seconds": {
            "description": "How long this credential may be used to open the socket — not how long the call may last. Default 300, maximum 3600.",
            "type": "integer"
          }
        },
        "required": [
          "participant"
        ],
        "type": "object"
      },
      "ClaimCallInput": {
        "properties": {
          "participant": {
            "description": "Who is answering, in your own vocabulary — a user id, an email, a desk number. Stored verbatim and never interpreted. Required.",
            "type": "string"
          }
        },
        "required": [
          "participant"
        ],
        "type": "object"
      },
      "Cost": {
        "properties": {
          "currency": {
            "description": "ISO currency code.",
            "type": "string"
          },
          "llm": {
            "description": "LLM tokens.",
            "type": "number"
          },
          "stt": {
            "description": "Speech-to-text.",
            "type": "number"
          },
          "telephony": {
            "description": "Carrier minutes.",
            "type": "number"
          },
          "total": {
            "description": "Sum of the components.",
            "type": "number"
          },
          "tts": {
            "description": "Speech synthesis.",
            "type": "number"
          }
        },
        "type": "object"
      },
      "DeletedObject": {
        "properties": {
          "deleted": {
            "description": "Always true.",
            "type": "boolean"
          },
          "id": {
            "description": "The deleted resource's ID.",
            "type": "string"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "deleted"
        ],
        "type": "object"
      },
      "Document": {
        "properties": {
          "bytes": {
            "description": "Extracted text size.",
            "type": "integer"
          },
          "chunks": {
            "description": "How many chunks it produced.",
            "type": "integer"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "error": {
            "description": "Why ingestion failed, when it did.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "knowledge_base_id": {
            "description": "Its knowledge base.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "source": {
            "description": "Which extractor produced it.",
            "enum": [
              "pdf",
              "docx",
              "web",
              "notion",
              "gdrive",
              "text",
              "markdown",
              "manual"
            ],
            "type": "string"
          },
          "status": {
            "description": "Ingestion state.",
            "enum": [
              "processing",
              "ready",
              "failed"
            ],
            "type": "string"
          },
          "title": {
            "description": "The document's title.",
            "type": "string"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          },
          "uri": {
            "description": "Where it came from.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "status"
        ],
        "type": "object"
      },
      "DocumentInput": {
        "description": "Provide exactly one of `text`, `content`, or `url`.",
        "properties": {
          "content": {
            "description": "Base64-encoded file bytes. Requires `filename`.",
            "type": "string"
          },
          "filename": {
            "description": "The original filename; selects the extractor.",
            "type": "string"
          },
          "language": {
            "description": "BCP-47 language tag.",
            "type": "string"
          },
          "max_pages": {
            "description": "When `url` is given, how many pages to crawl. Defaults to 1.",
            "type": "integer"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "text": {
            "description": "Inline text content.",
            "type": "string"
          },
          "title": {
            "description": "Overrides the detected title.",
            "type": "string"
          },
          "url": {
            "description": "A public http(s) URL to fetch and extract.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DocumentList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Document"
            },
            "type": "array"
          },
          "has_more": {
            "description": "Whether more pages exist.",
            "type": "boolean"
          },
          "next_cursor": {
            "description": "Pass as `starting_after` to fetch the next page.",
            "type": "string"
          },
          "object": {
            "const": "list",
            "type": "string"
          }
        },
        "required": [
          "object",
          "data"
        ],
        "type": "object"
      },
      "EndCallInput": {
        "properties": {
          "reason": {
            "description": "Why the call was ended.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Error": {
        "properties": {
          "code": {
            "description": "The stable, machine-readable classification. Branch on this.",
            "enum": [
              "invalid_request",
              "validation_error",
              "unauthenticated",
              "permission_denied",
              "not_found",
              "conflict",
              "idempotency_conflict",
              "rate_limited",
              "plan_limit_reached",
              "payload_too_large",
              "unsupported_media_type",
              "internal_error",
              "service_unavailable"
            ],
            "type": "string"
          },
          "detail": {
            "description": "What went wrong, specifically.",
            "type": "string"
          },
          "errors": {
            "description": "Per-field problems, when code is validation_error.",
            "items": {
              "properties": {
                "field": {
                  "description": "Which field.",
                  "type": "string"
                },
                "message": {
                  "description": "What is wrong with it.",
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "request_id": {
            "description": "Quote this when reporting a problem.",
            "type": "string"
          },
          "retry_after_seconds": {
            "description": "How long to wait, when rate limited.",
            "type": "integer"
          },
          "status": {
            "description": "The HTTP status code.",
            "type": "integer"
          },
          "title": {
            "description": "A short, human-readable summary.",
            "type": "string"
          },
          "type": {
            "description": "A URL documenting this error code.",
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "type": "object"
      },
      "Function": {
        "properties": {
          "description": {
            "description": "What it does and when the model should call it.",
            "type": "string"
          },
          "destructive": {
            "description": "Whether it has irreversible effects.",
            "type": "boolean"
          },
          "name": {
            "description": "Use this in an agent's `tools`.",
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "parameters": {
            "description": "JSON Schema for its arguments.",
            "type": "object"
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "FunctionList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Function"
            },
            "type": "array"
          },
          "has_more": {
            "description": "Whether more pages exist.",
            "type": "boolean"
          },
          "next_cursor": {
            "description": "Pass as `starting_after` to fetch the next page.",
            "type": "string"
          },
          "object": {
            "const": "list",
            "type": "string"
          }
        },
        "required": [
          "object",
          "data"
        ],
        "type": "object"
      },
      "GroupDefaults": {
        "description": "What this group says about each inheritable field. Every field is optional, and an absent one means the group has no opinion rather than a zero value. Sending `defaults` replaces the whole block, which is how an opinion is withdrawn: `{}` turns the group back into a plain label. The fields a group cannot set are the ones that make an agent that agent — name, description, languages (and with them the prompt, voice, greeting and speech vendors), mode, keypad, from_numbers and direction.",
        "properties": {
          "autonomy": {
            "description": "Who holds a `conversation` agent's end of the call. `agentic` is a model that searches the agent's knowledge bases before it answers and calls the tools in `tools`. `simple` is not a model at all: a person takes the call in their browser, and no speech recognizer, model or synthesizer is opened for it — the audio passes between the phone and the operator's WebSocket and nothing on this platform listens to it. An inbound number pointed at a simple agent rings rather than answers, and is picked up with `POST /v1/calls/{id}/claim`, exactly as a `webrtc` call is. `custom` is a model doing exactly what `capabilities` switches on, for the lines between the two — read the documents but never write to a system, call the functions but answer from what it was told, never offer a transfer because there is nobody behind it. Meaningless on the other three modes, which never reach a model. Defaults to agentic. Note that `simple` changed meaning: it used to mean a model that talks without retrieval or functions, which is `custom` with `search_knowledge` and `call_functions` off. Agents stored under the old meaning are read as exactly that and are returned with `autonomy` of `custom`.",
            "enum": [
              "agentic",
              "simple",
              "custom"
            ],
            "type": "string"
          },
          "capabilities": {
            "description": "What the agent may do besides talk, switch by switch. Read only when `autonomy` is `custom` — `agentic` resolves from its own name and `simple` has no model on the call to grant anything to. It is stored and returned on every agent whatever the autonomy is, so trying another one and coming back does not lose what you set. Sending it replaces the whole block. Omitted on a `custom` agent it means call control and nothing else — talking, plus hanging up and reaching a person — because four absent booleans are a block nobody wrote, not a decision to build a line that cannot even end its own call.",
            "properties": {
              "call_functions": {
                "description": "Call the functions in `tools`. A master switch over that list, not a second copy of it — which functions the agent has is still `tools`.",
                "type": "boolean"
              },
              "end_call": {
                "description": "Offer `end_call`, the other tool the runtime executes itself.",
                "type": "boolean"
              },
              "max_tool_rounds": {
                "description": "How many times one turn may call a tool and generate again. 1–10, or 0 for the default of 3. Every round is a model round trip with the caller listening to the filler phrase.",
                "type": "integer"
              },
              "retrieval_timeout_ms": {
                "description": "How long a knowledge lookup may take inside the turn. 50–2000, or 0 for the deployment's setting and then 250ms. Past it the turn answers ungrounded rather than leaving the line silent.",
                "type": "integer"
              },
              "search_knowledge": {
                "description": "Search the bases in `knowledge_base_ids` during a turn, and offer `search_knowledge_base`. Off, no lookup happens and none can time out.",
                "type": "boolean"
              },
              "transfer": {
                "description": "Offer `transfer_call`. Separate from `call_functions` because the runtime executes it against the call itself, not against one of your systems.",
                "type": "boolean"
              }
            },
            "type": "object"
          },
          "carrier": {
            "description": "The carrier members' outbound calls are placed through.",
            "type": "string"
          },
          "filler_phrase": {
            "description": "Spoken while a tool runs.",
            "type": "string"
          },
          "knowledge_base_ids": {
            "description": "Knowledge bases every member searches. An empty list is an opinion too: it means these agents ground against nothing.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "max_tokens": {
            "description": "Reply length cap: 1–4000, or 0 for the deployment's default.",
            "type": "integer"
          },
          "model": {
            "description": "The LLM every member uses unless it overrides it.",
            "type": "string"
          },
          "models": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Model per language provider, keyed by provider name.",
            "type": "object"
          },
          "pitch": {
            "description": "Pitch multiplier: 0.25–4, or 0 for the deployment's own.",
            "type": "number"
          },
          "record_calls": {
            "description": "Record members' calls. A member that was in the group before this was set keeps its own answer — see the API reference on why joining a group can never switch recording on.",
            "type": "boolean"
          },
          "speed": {
            "description": "Speech rate multiplier: 0.25–4, or 0 for the deployment's own.",
            "type": "number"
          },
          "temperature": {
            "description": "Sampling temperature (0–2).",
            "type": "number"
          },
          "tools": {
            "description": "Function names every member may call. An empty list means none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "transcribe_calls": {
            "description": "Transcribe members' `webrtc` calls. Seeded as an override on join for the same reason record_calls is.",
            "type": "boolean"
          },
          "transfer_number": {
            "description": "Where a human picks up. E.164.",
            "type": "string"
          },
          "webhook_url": {
            "description": "Overrides tenant webhooks for members' events. Must be https.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "KnowledgeBase": {
        "properties": {
          "chunks": {
            "description": "How many retrievable chunks exist.",
            "type": "integer"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "What it contains.",
            "type": "string"
          },
          "documents": {
            "description": "How many documents are indexed.",
            "type": "integer"
          },
          "embedding_model": {
            "description": "The model its vectors were built with.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "language": {
            "description": "Default language for its documents.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label.",
            "type": "string"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "type": "object"
      },
      "KnowledgeBaseInput": {
        "properties": {
          "description": {
            "description": "What this knowledge base contains.",
            "type": "string"
          },
          "language": {
            "description": "Default BCP-47 language for its documents.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label.",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "PostCallTestInput": {
        "properties": {
          "language": {
            "description": "The call's language, so the model knows what it is reading. Defaults to the agent's opening language.",
            "type": "string"
          },
          "text": {
            "description": "The call as lines of `role: what was said`. Either this or `transcript`.",
            "type": "string"
          },
          "transcript": {
            "description": "The call as turns. Either this or `text`.",
            "items": {
              "properties": {
                "role": {
                  "description": "`user` (or `caller`), `assistant` (or `agent`), or `tool`.",
                  "type": "string"
                },
                "source": {
                  "description": "`keypad` for a key the caller pressed rather than a word they said.",
                  "type": "string"
                },
                "text": {
                  "description": "What was said.",
                  "type": "string"
                }
              },
              "required": [
                "role",
                "text"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "variables": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "PostCallTestResult": {
        "properties": {
          "extracted": {
            "additionalProperties": true,
            "description": "Every extraction field the transcript settled, by name, in its declared type.",
            "type": "object"
          },
          "model": {
            "description": "What answered.",
            "type": "string"
          },
          "object": {
            "description": "`post_call_test`.",
            "type": "string"
          },
          "problems": {
            "description": "Answers dropped for not fitting their field's type. Empty when every answered field came back as typed.",
            "items": {
              "description": "One dropped answer and why.",
              "type": "string"
            },
            "type": "array"
          },
          "summary": {
            "description": "The summary, when the agent's settings ask for one.",
            "type": "string"
          },
          "took_ms": {
            "description": "How long the model took.",
            "type": "integer"
          }
        },
        "required": [
          "object",
          "model",
          "took_ms",
          "problems"
        ],
        "type": "object"
      },
      "Project": {
        "properties": {
          "budget_tokens": {
            "description": "Tokens this project may spend in a calendar month. Zero means no budget.\n\nGoing over refuses this project's calls with a 402 `project_budget_exhausted` for the rest of the month. It does not touch the account's balance and does not affect any other project — a runaway agent costs its own cost centre its month, not the account its quarter.\n\nEnforced on settled spending, so a project can overshoot by up to one settlement window.",
            "type": "integer"
          },
          "color": {
            "description": "Dots this project in a list. Omit for none.",
            "enum": [
              "slate",
              "red",
              "orange",
              "amber",
              "green",
              "teal",
              "blue",
              "indigo",
              "violet",
              "pink"
            ],
            "type": "string"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "default": {
            "description": "Marks the project that owns everything not filed anywhere else. Exactly one per account, and it cannot be archived or deleted — PATCH another project with `default: true` to hand the marker over, which is also how this one is freed to be deleted.",
            "type": "boolean"
          },
          "description": {
            "description": "What work belongs here.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label — what this cost centre is called on your own bill.",
            "type": "string"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "slug": {
            "description": "The short handle it is addressable by. Unique within the account, derived from the name when not supplied.",
            "type": "string"
          },
          "status": {
            "description": "`active` takes new work; `archived` does not, and keeps everything the project has already cost.",
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "slug",
          "status"
        ],
        "type": "object"
      },
      "ProjectInput": {
        "properties": {
          "budget_tokens": {
            "description": "Monthly spending cap in tokens. Zero removes it.",
            "type": "integer"
          },
          "color": {
            "description": "Dots this project in a list. Omit for none.",
            "enum": [
              "slate",
              "red",
              "orange",
              "amber",
              "green",
              "teal",
              "blue",
              "indigo",
              "violet",
              "pink"
            ],
            "type": "string"
          },
          "default": {
            "description": "Set to `true` on PATCH to make this the account's default project, unmarking whichever held it.\n\nNothing is re-attributed: the old default keeps every call already billed to it, and what changes is where future unfiled work lands — which is what frees it to be archived or deleted.\n\n`false` is refused, because every account has a default: name the project that should take over instead. Refused on create, and refused for an archived project, which takes no new work and so cannot be where unfiled work goes.",
            "type": "boolean"
          },
          "description": {
            "description": "What work belongs here.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label. Required on create.",
            "type": "string"
          },
          "slug": {
            "description": "The handle. Derived from the name when omitted; lowercase letters, numbers and single hyphens only.",
            "type": "string"
          },
          "status": {
            "description": "`active` or `archived`.",
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RealtimeToken": {
        "properties": {
          "agent": {
            "$ref": "#/components/schemas/RealtimeTokenAgent"
          },
          "agent_id": {
            "description": "The agent answering.",
            "type": "string"
          },
          "call_id": {
            "description": "The call this token — and only this token — may join.",
            "type": "string"
          },
          "direction": {
            "description": "Which way the call is going. Only on a token from `/calls/{id}/token`.",
            "type": "string"
          },
          "expires_at": {
            "description": "When the token stops being able to open the socket.",
            "format": "date-time",
            "type": "string"
          },
          "from": {
            "description": "The caller ID this call presents. Only on a token from `/calls/{id}/token`.",
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "realtime_url": {
            "description": "The socket without the credential.",
            "type": "string"
          },
          "status": {
            "description": "The call's status as the token was issued. Only on a token from `/calls/{id}/token`.",
            "type": "string"
          },
          "subprotocol": {
            "description": "The WebSocket subprotocol to offer. Required by the handshake.",
            "type": "string"
          },
          "to": {
            "description": "The number on the other end. Only on a token from `/calls/{id}/token`.",
            "type": "string"
          },
          "token": {
            "description": "The credential on its own, for a client that builds its own URL.",
            "type": "string"
          },
          "url": {
            "description": "The realtime WebSocket with the credential already in it. Connect to this; there is nothing to assemble.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RealtimeTokenAgent": {
        "properties": {
          "id": {
            "type": "string"
          },
          "language": {
            "description": "For picking a locale in your interface.",
            "type": "string"
          },
          "name": {
            "description": "For labelling the call in your interface.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RealtimeTokenInput": {
        "properties": {
          "agent_id": {
            "description": "The agent to talk to. Required.",
            "type": "string"
          },
          "language": {
            "description": "Opens this call in one of the agent's languages. Defaults to the first.",
            "type": "string"
          },
          "metadata": {
            "description": "Stored with the call.",
            "type": "object"
          },
          "project_id": {
            "description": "Attributes the call to a cost centre, overriding the agent's own.",
            "type": "string"
          },
          "ttl_seconds": {
            "description": "How long the token may be used to open the socket. 1–3600, default 300. It does not bound the call: once the handshake is done, the socket holds the session.",
            "type": "integer"
          },
          "variables": {
            "description": "Seeds the agent's context — a name, an order number the page already knows.",
            "type": "object"
          }
        },
        "required": [
          "agent_id"
        ],
        "type": "object"
      },
      "RecordingLink": {
        "properties": {
          "call_id": {
            "description": "The call this plays.",
            "type": "string"
          },
          "content_type": {
            "description": "Always audio/wav.",
            "type": "string"
          },
          "duration_seconds": {
            "description": "How long the recording runs.",
            "type": "number"
          },
          "expires_at": {
            "description": "After this the link is refused.",
            "format": "date-time",
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "url": {
            "description": "Play this. Signed for one call, read-only, and short-lived — it needs no Authorization header, which is the whole point of it.",
            "type": "string"
          }
        },
        "required": [
          "url",
          "expires_at"
        ],
        "type": "object"
      },
      "Rendezvous": {
        "properties": {
          "created_at": {
            "description": "",
            "format": "date-time",
            "type": "string"
          },
          "direction": {
            "description": "Always `web`.",
            "type": "string"
          },
          "id": {
            "description": "The call ID.",
            "type": "string"
          },
          "metadata": {
            "description": "Whatever was sent.",
            "type": "object"
          },
          "object": {
            "type": "string"
          },
          "project_id": {
            "description": "The cost centre it was billed to.",
            "type": "string"
          },
          "realtime_url": {
            "description": "The socket both ends join. It carries no credential — `POST /v1/calls/{id}/seat` mints one per seat, because the two ends are handed out at different moments to different people.",
            "type": "string"
          },
          "record": {
            "description": "What this call is keeping.",
            "type": "boolean"
          },
          "rendezvous": {
            "description": "Always true. It is what says this call has no agent.",
            "type": "boolean"
          },
          "rings_until": {
            "description": "When it stops ringing.",
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "description": "`ringing` on creation.",
            "type": "string"
          },
          "transcribe": {
            "description": "What this call is keeping.",
            "type": "boolean"
          },
          "transport": {
            "description": "Always `browser`.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RendezvousInput": {
        "properties": {
          "metadata": {
            "description": "Your own identifiers, kept verbatim and echoed on every event about this call. There are no `variables` on a rendezvous: they fill an agent's {{placeholders}}, and this call has no prompt to put one in.",
            "type": "object"
          },
          "project_id": {
            "description": "The cost centre to bill. Omitted, the account's default answers; there is no agent to inherit one from.",
            "type": "string"
          },
          "record": {
            "description": "Keep the audio. Off by default: the kept copy of a peer-to-peer call is uplinked by each person's own browser, so a call that asks for nothing stores nothing and spends no bandwidth on it. Refused when the deployment has no recording storage, rather than accepted and quietly dropped.",
            "type": "boolean"
          },
          "ring_seconds": {
            "description": "How long it rings before nobody has answered. Default 30, maximum 120. Asking for more is refused rather than clamped.",
            "type": "integer"
          },
          "transcribe": {
            "description": "Keep what was said. Off by default, and refused when the deployment has no speech recognizer. Both ends appear in the transcript as `caller` and `responder` — on this call both of them are people.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "RunWorkflowInput": {
        "properties": {
          "call_id": {
            "description": "Associates the run with a call.",
            "type": "string"
          },
          "input": {
            "description": "Values bound to `input.*` in step templates.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "SearchInput": {
        "properties": {
          "filter": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Exact-match metadata filter.",
            "type": "object"
          },
          "query": {
            "description": "What to look up, phrased as a caller would ask it.",
            "type": "string"
          },
          "top_k": {
            "description": "How many passages to return (1–25).",
            "type": "integer"
          }
        },
        "required": [
          "query"
        ],
        "type": "object"
      },
      "SearchResponse": {
        "properties": {
          "grounding": {
            "description": "The assembled block the model would receive.",
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "query": {
            "description": "The query that was run.",
            "type": "string"
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/SearchResult"
            },
            "type": "array"
          },
          "took_ms": {
            "description": "Retrieval time in milliseconds.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SearchResult": {
        "properties": {
          "document_id": {
            "description": "The document it belongs to.",
            "type": "string"
          },
          "lexical_score": {
            "description": "Term-overlap coverage.",
            "type": "number"
          },
          "score": {
            "description": "The blended relevance score.",
            "type": "number"
          },
          "section": {
            "description": "Its heading path.",
            "type": "string"
          },
          "text": {
            "description": "The passage.",
            "type": "string"
          },
          "title": {
            "description": "Its document's title.",
            "type": "string"
          },
          "uri": {
            "description": "Where it came from.",
            "type": "string"
          },
          "vector_score": {
            "description": "Embedding similarity.",
            "type": "number"
          }
        },
        "type": "object"
      },
      "TimeSeries": {
        "properties": {
          "bucket": {
            "description": "Bucket size.",
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "points": {
            "items": {
              "type": "object"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "Transcript": {
        "properties": {
          "call_id": {
            "type": "string"
          },
          "entries": {
            "items": {
              "$ref": "#/components/schemas/TranscriptEntry"
            },
            "type": "array"
          },
          "object": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "turns": {
            "description": "How many caller turns the conversation had.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "TranscriptEntry": {
        "properties": {
          "at": {
            "description": "When.",
            "format": "date-time",
            "type": "string"
          },
          "latency_ms": {
            "description": "How long the turn took.",
            "type": "integer"
          },
          "role": {
            "description": "Who spoke.",
            "enum": [
              "user",
              "agent",
              "tool"
            ],
            "type": "string"
          },
          "text": {
            "description": "What was said.",
            "type": "string"
          },
          "tool_name": {
            "description": "The tool, when role is tool.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "WebhookDelivery": {
        "properties": {
          "attempts": {
            "description": "How many tries so far.",
            "type": "integer"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "endpoint_id": {
            "description": "Which endpoint.",
            "type": "string"
          },
          "error": {
            "description": "The last failure.",
            "type": "string"
          },
          "event": {
            "description": "The event type.",
            "type": "string"
          },
          "event_id": {
            "description": "The event's ID; use it to deduplicate.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "next_attempt_at": {
            "description": "When the next retry is due.",
            "format": "date-time",
            "type": "string"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "response_code": {
            "description": "The endpoint's last HTTP status.",
            "type": "integer"
          },
          "status": {
            "description": "Delivery state.",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ],
            "type": "string"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "status"
        ],
        "type": "object"
      },
      "WebhookDeliveryList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            },
            "type": "array"
          },
          "has_more": {
            "description": "Whether more pages exist.",
            "type": "boolean"
          },
          "next_cursor": {
            "description": "Pass as `starting_after` to fetch the next page.",
            "type": "string"
          },
          "object": {
            "const": "list",
            "type": "string"
          }
        },
        "required": [
          "object",
          "data"
        ],
        "type": "object"
      },
      "WebhookEndpoint": {
        "properties": {
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "What this endpoint is for.",
            "type": "string"
          },
          "disabled_reason": {
            "description": "Why delivery was suspended.",
            "type": "string"
          },
          "enabled": {
            "description": "Disabled endpoints receive nothing.",
            "type": "boolean"
          },
          "events": {
            "description": "Subscribed event types. `*` and `call.*` are accepted.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "secret": {
            "description": "The signing secret. Returned only when the endpoint is created.",
            "type": "string"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          },
          "url": {
            "description": "Where events are delivered. Must be https.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "url"
        ],
        "type": "object"
      },
      "WebhookInput": {
        "properties": {
          "description": {
            "description": "What this endpoint is for.",
            "type": "string"
          },
          "enabled": {
            "description": "Defaults to true.",
            "type": "boolean"
          },
          "events": {
            "description": "Defaults to [\"*\"].",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "secret": {
            "description": "PUT only. The signing secret to use, at least 16 characters. Supplying the value your service already holds is what makes declaring an endpoint on every boot safe; omit it and one is generated and returned on the creating call.",
            "type": "string"
          },
          "url": {
            "description": "Must be https. On PUT this is the key the upsert matches on.",
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "WebhookTestResult": {
        "properties": {
          "delivered": {
            "description": "Whether the endpoint accepted the event.",
            "type": "boolean"
          },
          "endpoint_id": {
            "type": "string"
          },
          "error": {
            "description": "Why delivery failed, when it did.",
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "response_code": {
            "description": "The endpoint's HTTP status.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Workflow": {
        "properties": {
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "What it does. Written for the model when exposed as a tool.",
            "type": "string"
          },
          "enabled": {
            "description": "Disabled workflows refuse to run.",
            "type": "boolean"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "input_schema": {
            "description": "JSON Schema for the trigger arguments.",
            "type": "object"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label.",
            "type": "string"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "on_error": {
            "description": "What happens when a step fails.",
            "enum": [
              "abort",
              "continue"
            ],
            "type": "string"
          },
          "steps": {
            "items": {
              "$ref": "#/components/schemas/WorkflowStep"
            },
            "type": "array"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "steps"
        ],
        "type": "object"
      },
      "WorkflowInput": {
        "properties": {
          "description": {
            "description": "What it does.",
            "type": "string"
          },
          "enabled": {
            "description": "Defaults to true.",
            "type": "boolean"
          },
          "input_schema": {
            "type": "object"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "name": {
            "description": "A human label.",
            "type": "string"
          },
          "on_error": {
            "description": "Defaults to abort.",
            "enum": [
              "abort",
              "continue"
            ],
            "type": "string"
          },
          "steps": {
            "items": {
              "$ref": "#/components/schemas/WorkflowStep"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "WorkflowRun": {
        "properties": {
          "call_id": {
            "description": "The associated call, if any.",
            "type": "string"
          },
          "created_at": {
            "description": "When it was created.",
            "format": "date-time",
            "type": "string"
          },
          "duration_ms": {
            "description": "Wall-clock time.",
            "type": "integer"
          },
          "error": {
            "description": "Why the run failed.",
            "type": "string"
          },
          "id": {
            "description": "Unique, prefixed identifier.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Arbitrary string key/value pairs you can attach and read back.",
            "type": "object"
          },
          "object": {
            "description": "The resource type.",
            "type": "string"
          },
          "status": {
            "description": "Outcome.",
            "enum": [
              "succeeded",
              "partial",
              "failed",
              "running"
            ],
            "type": "string"
          },
          "steps": {
            "items": {
              "properties": {
                "attempts": {
                  "description": "How many tries it took.",
                  "type": "integer"
                },
                "error": {
                  "description": "Why it failed.",
                  "type": "string"
                },
                "id": {
                  "description": "The step ID.",
                  "type": "string"
                },
                "output": {
                  "type": "object"
                },
                "skipped_because": {
                  "description": "Which condition excluded it.",
                  "type": "string"
                },
                "status": {
                  "description": "Step outcome.",
                  "enum": [
                    "succeeded",
                    "failed",
                    "skipped"
                  ],
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "updated_at": {
            "description": "When it last changed.",
            "format": "date-time",
            "type": "string"
          },
          "workflow_id": {
            "description": "Which workflow ran.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "workflow_id",
          "status"
        ],
        "type": "object"
      },
      "WorkflowRunList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/WorkflowRun"
            },
            "type": "array"
          },
          "has_more": {
            "description": "Whether more pages exist.",
            "type": "boolean"
          },
          "next_cursor": {
            "description": "Pass as `starting_after` to fetch the next page.",
            "type": "string"
          },
          "object": {
            "const": "list",
            "type": "string"
          }
        },
        "required": [
          "object",
          "data"
        ],
        "type": "object"
      },
      "WorkflowStep": {
        "properties": {
          "continue_on_error": {
            "description": "Treat a failure here as non-fatal.",
            "type": "boolean"
          },
          "description": {
            "description": "What this step is for.",
            "type": "string"
          },
          "id": {
            "description": "Unique within the workflow; later steps reference it as `steps.\u003cid\u003e.*`.",
            "type": "string"
          },
          "max_attempts": {
            "description": "Retries, including the first attempt.",
            "type": "integer"
          },
          "save_as": {
            "description": "Also bind the output to `vars.\u003cname\u003e`.",
            "type": "string"
          },
          "type": {
            "description": "Which action runs.",
            "enum": [
              "book_appointment",
              "update_crm",
              "send_email",
              "trigger_webhook",
              "query_db",
              "set"
            ],
            "type": "string"
          },
          "when": {
            "description": "A condition; the step is skipped when it is false.",
            "type": "string"
          },
          "with": {
            "description": "Parameters. String values are interpolated with `{{ path }}`.",
            "type": "object"
          }
        },
        "required": [
          "id",
          "type"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "description": "An API key (`vsk_...`) or a JWT.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "contact": {
      "name": "Sphoro Voice Support",
      "url": "https://voice.sphoro.com/docs/"
    },
    "description": "The Sphoro Voice API.\n\n## Authentication\n\nEvery request carries an API key as a bearer token:\n\n    Authorization: Bearer vsk_...\n\n## Errors\n\nFailures return `application/problem+json` with a stable `code` and a\n`request_id`. Branch on the code, never on the message text — messages improve,\ncodes do not change.\n\n## Pagination\n\nList endpoints are cursor-paginated: pass `limit` and `starting_after`,\nand follow `next_cursor` while `has_more` is true.\n\n## Idempotency\n\nSend an `Idempotency-Key` header on POST requests. A retry with the same key\nreturns the original response instead of repeating the side effect.\n\n## Rate limits\n\nResponses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and\n`X-RateLimit-Reset`. A 429 includes `Retry-After`.\n\n## Streaming\n\n`GET /v1/calls/{id}/events` is a Server-Sent Events stream that supports\n`Last-Event-ID` replay. `GET /v1/realtime` is a WebSocket carrying\nbidirectional audio.",
    "title": "Sphoro Voice API",
    "version": "1.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/healthz": {
      "get": {
        "operationId": "health",
        "responses": {
          "200": {
            "description": "The service is up."
          }
        },
        "security": [],
        "summary": "Liveness probe",
        "tags": [
          "Analytics"
        ]
      }
    },
    "/v1/agent_groups": {
      "get": {
        "operationId": "listAgentGroups",
        "parameters": [
          {
            "description": "How many items to return (1–100, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A cursor from a previous response's `next_cursor`.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/AgentGroup"
                      },
                      "type": "array"
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "object": {
                      "const": "list",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "List agent groups",
        "tags": [
          "Agent groups"
        ]
      },
      "post": {
        "description": "Only `name` is required. A group with no `defaults` is a plain label, which is a perfectly good thing to want — it filters a list and colours it, and forcing an opinion on it would mean inventing one to get that.",
        "operationId": "createAgentGroup",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentGroupInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentGroup"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create an agent group",
        "tags": [
          "Agent groups"
        ]
      }
    },
    "/v1/agent_groups/{id}": {
      "delete": {
        "description": "Refused with 409 while any agent is still in the group. Deleting it would change what every member runs on — each inherited field would revert to whatever that agent's own stored value happens to be — and there is no record of what they were inheriting, so it could not be undone. Move the members or clear their `group_id` first; the members endpoint lists them.",
        "operationId": "deleteAgentGroup",
        "parameters": [
          {
            "description": "The agent group ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedObject"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete an agent group",
        "tags": [
          "Agent groups"
        ]
      },
      "get": {
        "operationId": "getAgentGroup",
        "parameters": [
          {
            "description": "The agent group ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentGroup"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve an agent group",
        "tags": [
          "Agent groups"
        ]
      },
      "patch": {
        "description": "Only the fields present in the request are changed; omitted fields keep their current value.",
        "operationId": "updateAgentGroup",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentGroupInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentGroup"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update an agent group",
        "tags": [
          "Agent groups"
        ]
      }
    },
    "/v1/agent_groups/{id}/members": {
      "get": {
        "description": "Every agent in the group, each with the fields it has claimed and the fields on which it actually differs from the group's defaults — that is, the ones adopting the defaults would change. `differing` counts the agents with at least one.\n\nNot paginated: the count is the point, and a count that says 3 because the first page held 3 is worse than none.",
        "operationId": "listAgentGroupMembers",
        "parameters": [
          {
            "description": "The agent group ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentGroupMembers"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List a group's agents",
        "tags": [
          "Agent groups"
        ]
      }
    },
    "/v1/agents": {
      "get": {
        "operationId": "listAgents",
        "parameters": [
          {
            "description": "How many items to return (1–100, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A cursor from a previous response's `next_cursor`.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/Agent"
                      },
                      "type": "array"
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "object": {
                      "const": "list",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "List agents",
        "tags": [
          "Agents"
        ]
      },
      "post": {
        "description": "Creates a voice agent. Only `name` and one language carrying a `system_prompt` are required; every other field has a sensible default.",
        "operationId": "createAgent",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create an agent",
        "tags": [
          "Agents"
        ]
      }
    },
    "/v1/agents/{id}": {
      "delete": {
        "description": "The agent stops being offered: it leaves every list, stops counting against the account's agent limit, and can neither answer nor place another call. Reading it afterwards is a 404.\n\nIt is not destroyed, and `POST /agents/{id}/restore` puts it back. What it keeps is its identity, because everything it did still points at it — the calls in the log, their recordings, the audit trail, and any campaign it ran. Removing the record would turn all of that into history naming an id nothing can resolve.\n\nPhone numbers are not released. A number left pointed at a deleted agent stops being answered, so re-point it before deleting the agent behind it.\n\nA call already in progress is unaffected and runs to its end.",
        "operationId": "deleteAgent",
        "parameters": [
          {
            "description": "The agent ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedObject"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete an agent",
        "tags": [
          "Agents"
        ]
      },
      "get": {
        "description": "Returns the agent's own stored values. Pass `effective=true` to see what a call would actually run on instead — the same record with its group's defaults filled in wherever the agent has not claimed the field in `overrides`.\n\nStored is the default deliberately. A client that reads an agent and writes it back must not have baked its group's values into it on the way, because that agent would then stop tracking the group and nothing in the round trip would have said so.",
        "operationId": "getAgent",
        "parameters": [
          {
            "description": "The agent ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Resolve the agent against its group before returning it. Never changes what is stored.",
            "in": "query",
            "name": "effective",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve an agent",
        "tags": [
          "Agents"
        ]
      },
      "patch": {
        "description": "Only the fields present in the request are changed; omitted fields keep their current value.",
        "operationId": "updateAgent",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update an agent",
        "tags": [
          "Agents"
        ]
      }
    },
    "/v1/agents/{id}/post_call/test": {
      "post": {
        "description": "Runs the agent's `post_call` settings — the summary and every extraction field — against a transcript you supply, and answers with what the model wrote down. Nothing is stored and no call is placed.\n\nSend the call as `transcript`, a list of `{role, text}` turns, or as `text`, lines of `role: what was said`. `user`/`caller` and `assistant`/`agent` are both accepted. At least one caller turn is required: a call nobody spoke on has nothing to read back.\n\n`problems` lists every answer dropped for not fitting its field's type — a `number` the model answered in words, an `enum` value not among the options. Empty means every answered field came back as typed.",
        "operationId": "testPostCall",
        "parameters": [
          {
            "description": "The agent ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostCallTestInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostCallTestResult"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Test an agent's post-call settings",
        "tags": [
          "Agents"
        ]
      }
    },
    "/v1/agents/{id}/restore": {
      "post": {
        "description": "Puts back an agent removed with `DELETE /agents/{id}`, and answers with it.\n\nThe agent alone. Anything detached on the way out stays detached — a phone number most of all, since another agent may have been answering it since.\n\nRestoring an agent that was never deleted succeeds and changes nothing, so a client may retry it safely.",
        "operationId": "restoreAgent",
        "parameters": [
          {
            "description": "The agent ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Restore a deleted agent",
        "tags": [
          "Agents"
        ]
      }
    },
    "/v1/analytics/csat": {
      "post": {
        "description": "Attaches a 1–5 score, and optionally a comment, to a call that has already happened. Ratings are collected on your side — a post-call SMS, an IVR keypress, a follow-up form — and posted here; this is the only way a score reaches the platform, and what fills the Call ratings screen in the portal.\n\nRe-rating the same call replaces the previous score rather than counting twice, so a retry is safe. Delivers a `csat.received` webhook event.",
        "operationId": "recordCSAT",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CSATInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CSATAccepted"
                }
              }
            },
            "description": "The rating was accepted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Record a satisfaction rating",
        "tags": [
          "Analytics"
        ]
      }
    },
    "/v1/analytics/overview": {
      "get": {
        "operationId": "getAnalyticsOverview",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsOverview"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Usage and quality overview",
        "tags": [
          "Analytics"
        ]
      }
    },
    "/v1/analytics/timeseries": {
      "get": {
        "operationId": "getAnalyticsTimeSeries",
        "parameters": [
          {
            "description": "Bucket size as a duration, between 1m and 24h.",
            "in": "query",
            "name": "bucket",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimeSeries"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Bucketed call volume and cost",
        "tags": [
          "Analytics"
        ]
      }
    },
    "/v1/calls": {
      "get": {
        "operationId": "listCalls",
        "parameters": [
          {
            "description": "How many items to return (1–100, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A cursor from a previous response's `next_cursor`.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/Call"
                      },
                      "type": "array"
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "object": {
                      "const": "list",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "List calls",
        "tags": [
          "Calls"
        ]
      },
      "post": {
        "description": "Creates a call. Omit `to` for a `web` call and connect to the returned `realtime_url`.",
        "operationId": "createCall",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Start a call",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/rendezvous": {
      "post": {
        "description": "Creates a call with no agent on it: two browsers, introduced to each other, with the WebRTC handshake passed between them and the audio going directly from one to the other. Nothing on this platform hears it unless the call asks to be recorded or transcribed, in which case each client uplinks a copy of its own microphone.\n\nUse this instead of creating an agent for person-to-person calling. An agent is a model's configuration — a prompt, a voice, tools, a language — and a call between two people reads none of it; requiring one meant keeping a record whose settings nobody on the call uses, and one edit in the portal away from a customer who was promised a colleague reaching a model instead.\n\nThe call is created `ringing`. Hand out its two ends with `POST /v1/calls/{id}/seat` — the caller's immediately, the responder's when somebody answers. Everything else about it is an ordinary call: `GET /v1/calls/{id}`, `/end`, `/give-up`, `/recording`, the ringing feed and every `call.*` webhook work unchanged, with no `agent_id` in the payload.",
        "operationId": "createRendezvous",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RendezvousInput"
              }
            }
          },
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Rendezvous"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Place a call between two people",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/ringing": {
      "get": {
        "description": "Server-Sent Events, one frame per ringing telephone. `/calls/{id}/events` needs an id, and the whole problem an inbound call poses is learning about a call whose id nobody knows yet.\n\nThe stream opens with whatever is already ringing, so a tab that reloads mid-ring is still told. Frames are `{\"type\":\"ringing\",\"id\",\"from\",\"to\",\"direction\",\"agent\",\"rings_for_ms\"}` and `{\"type\":\"settled\",\"id\",\"reason\",\"by\"}`. Key by `id`: a call may be announced twice and a duplicate is nothing, where a miss is a lost customer.\n\nTime out on `rings_for_ms`, not `rings_until` — an absolute deadline is only as good as the clock reading it, and that clock belongs to whatever laptop your product is open on.\n\nOnly calls a browser can actually answer are offered.\n\n**The heartbeat.** Every five seconds the stream also carries a *named* SSE event, `event: heartbeat`, whose data is `{\"type\":\"heartbeat\",\"ringing\":[ …ringing frames… ]}` — everything you should have a telephone on screen for, right now. Reconcile against it: anything listed and not shown is a ringing frame you never received, and anything shown and not listed is a `settled` you never received. Both happen. A feed that falls behind has its overflow discarded rather than stalling a live call's media path, and a connection that dies without saying so delivers nothing at all while still looking open — which no stream of ringing frames can distinguish from a quiet account.\n\nBecause a heartbeat arrives on a schedule even when nothing is happening, silence is diagnostic: stop receiving them and the stream is gone whatever it claims, so close it and open another. Twenty seconds is a reasonable ceiling.\n\nIt is a named event, so a client reading only `onmessage` never sees it and is unaffected. Do not filter or batch this stream in a proxy — the heartbeat is what makes an inbound call reliable rather than likely.",
        "operationId": "streamRingingCalls",
        "parameters": [
          {
            "description": "Who is watching, as a member id — the one the portal shows, and the same one you send as `member_id` when joining a call. A number that is somebody's direct line is theirs alone, and this is what decides whose feed it appears on.\n\nOmitting it is not \"everybody\": a watcher who has not said who they are is shown no call to any assigned number at all, which is the safe reading and is why a telecaller's own line appears never to ring on a feed opened without one. Leave it off only for a dashboard watching the shared pool.",
            "in": "query",
            "name": "member_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "The event stream."
          }
        },
        "summary": "Watch for calls waiting to be answered",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}": {
      "get": {
        "operationId": "getCall",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve a call",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/claim": {
      "post": {
        "description": "Answers a `ringing` call and moves it to `in_progress`. This is the only place the decision is made: two people pressing Answer at the same instant both arrive here, one changes the record and the other is told 409, which is an ordinary outcome and not an error. Claiming a call you already hold returns it unchanged. A call whose ring window has passed is recorded as missed and refused.",
        "operationId": "claimCall",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClaimCallInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Answer a ringing call",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/end": {
      "post": {
        "description": "Hangs up a call that is still in progress.",
        "operationId": "endCall",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EndCallInput"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "End a call",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/events": {
      "get": {
        "description": "A Server-Sent Events stream of the call's events. Send `Last-Event-ID` to replay anything missed across a reconnect. The stream ends with an `event: done` frame after `call.ended`.",
        "operationId": "streamCallEvents",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "An event stream."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "summary": "Stream call events",
        "tags": [
          "Calls"
        ]
      },
      "post": {
        "description": "Tells a call in progress that something happened outside it. On an agent with a flow, the properties become the call's variables and an `event` edge on the current node moves the call, after which the agent speaks first at the next quiet moment. 202 accepted; 404 the call is not live on this node (it has ended, or is held elsewhere); 409 the agent has no flow.",
        "operationId": "injectCallEvent",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallEventInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object"
                }
              }
            },
            "description": "A call_event acknowledgement."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Inject an event into a live call",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/give-up": {
      "post": {
        "description": "Ends a `ringing` call nobody took, as `completed` with `end_reason: missed` — distinct from `failed`, because nobody being free is an ordinary outcome of ringing a room full of busy people. Refused while the call is still within its ring window, so a client with a fast clock cannot cut somebody off halfway through answering.",
        "operationId": "giveUpCall",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Record that nobody answered",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/recording": {
      "get": {
        "description": "The call's audio as WAV. Authenticate with `Authorization: Bearer` as usual — or, for a link a browser can play, use `/calls/{id}/recording_url` instead: `\u003caudio src\u003e` cannot send a header, so this route handed straight to a player is a 401.",
        "operationId": "getRecording",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "audio/wav": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The audio."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "summary": "Download a recording",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/recording_url": {
      "get": {
        "description": "A signed URL for this call's audio that needs no header, so it can go straight into `\u003caudio src\u003e` or a download link. The token names one call, carries read access only, and expires — 15 minutes by default, `ttl_seconds` to change it. Mint one when somebody asks to listen rather than storing it beside the call: a link kept in a database has expired long before anyone clicks it.",
        "operationId": "getRecordingURL",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How long the link stays valid. Default 900, maximum 21600.",
            "in": "query",
            "name": "ttl_seconds",
            "required": false,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecordingLink"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Get a playable recording link",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/seat": {
      "post": {
        "description": "Mints the short-lived credential one browser opens a rendezvous with, and says which of its two ends that browser is taking. Hand `url` to the client and nothing else.\n\n`role: \"caller\"` is whoever placed the call. It does not answer anything and leaves the call ringing — take it as soon as the call exists, because a rendezvous with nobody on the caller's end is a call that ends rather than ringing at an empty room.\n\n`role: \"responder\"` is whoever answered, and taking it *is* answering: it claims the call through the same serialization `/calls/{id}/claim` uses, so two people pressing Answer in the same instant produce one connection and one 409. `participant` is required on this seat and is recorded as `claimed_by`. The responder makes the WebRTC offer, which is why the seat is named here rather than left to the two browsers to work out between themselves.\n\nOnly a rendezvous has seats. For a call whose agent is answered by a person, use `POST /v1/calls/{id}/token`.",
        "operationId": "takeCallSeat",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallSeatInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallSeat"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Take one end of a rendezvous",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/token": {
      "post": {
        "description": "Mints the short-lived credential a browser opens an existing call's audio with, so a telecaller signed into *your* product can be the person on our end of a phone call. The response is the same shape `POST /v1/realtime/tokens` returns: hand `url` to the browser client and nothing else.\n\nOnly a call whose agent is answered by a person — `autonomy: \"simple\"` — can be joined. An agentic call already has both ends, and a webrtc call is a rendezvous between two browsers rather than a leg carrying audio; joining either connects, reports itself live and carries nothing, so both are refused.\n\nJoining a `ringing` call answers it, through the same serialization `/calls/{id}/claim` uses. A call somebody else already holds is a 409 — a softphone has one browser seat, and a second joiner would displace the first rather than join them. The holder may reconnect as often as their network makes them.",
        "operationId": "createCallToken",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallTokenInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RealtimeToken"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Join a call from a browser",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/calls/{id}/transcript": {
      "get": {
        "operationId": "getTranscript",
        "parameters": [
          {
            "description": "The call ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transcript"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve a transcript",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/functions": {
      "get": {
        "description": "The functions available to this account. Use these names in an agent's `tools`.",
        "operationId": "listFunctions",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FunctionList"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List callable functions",
        "tags": [
          "Functions"
        ]
      }
    },
    "/v1/knowledge_bases": {
      "get": {
        "operationId": "listKnowledgeBases",
        "parameters": [
          {
            "description": "How many items to return (1–100, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A cursor from a previous response's `next_cursor`.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/KnowledgeBase"
                      },
                      "type": "array"
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "object": {
                      "const": "list",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "List knowledge bases",
        "tags": [
          "Knowledge bases"
        ]
      },
      "post": {
        "description": "Creates an empty knowledge base. Add content with the documents endpoint.",
        "operationId": "createKnowledgeBase",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KnowledgeBaseInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeBase"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create a knowledge base",
        "tags": [
          "Knowledge bases"
        ]
      }
    },
    "/v1/knowledge_bases/{id}": {
      "delete": {
        "operationId": "deleteKnowledgeBase",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedObject"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete a knowledge base and everything in it",
        "tags": [
          "Knowledge bases"
        ]
      },
      "get": {
        "operationId": "getKnowledgeBase",
        "parameters": [
          {
            "description": "The knowledge base ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeBase"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve a knowledge base",
        "tags": [
          "Knowledge bases"
        ]
      }
    },
    "/v1/knowledge_bases/{id}/documents": {
      "get": {
        "operationId": "listDocuments",
        "parameters": [
          {
            "description": "The knowledge base ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List documents",
        "tags": [
          "Knowledge bases"
        ]
      },
      "post": {
        "description": "Adds content from exactly one of `text`, `content` (base64 file bytes plus `filename`), or `url`. PDF, DOCX, HTML, Markdown, and plain text are extracted automatically.",
        "operationId": "ingestDocument",
        "parameters": [
          {
            "description": "The knowledge base ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Ingest a document",
        "tags": [
          "Knowledge bases"
        ]
      }
    },
    "/v1/knowledge_bases/{id}/documents/{doc}": {
      "delete": {
        "description": "Removes the document and every embedding it contributed.",
        "operationId": "deleteDocument",
        "parameters": [
          {
            "description": "The knowledge base ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The document ID.",
            "in": "path",
            "name": "doc",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedObject"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete a document",
        "tags": [
          "Knowledge bases"
        ]
      }
    },
    "/v1/knowledge_bases/{id}/search": {
      "post": {
        "description": "Runs exactly the retrieval an agent runs, returning the passages, their scores, and the assembled grounding block. Use it to answer \"why did my agent say that?\".",
        "operationId": "searchKnowledgeBase",
        "parameters": [
          {
            "description": "The knowledge base ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Search a knowledge base",
        "tags": [
          "Knowledge bases"
        ]
      }
    },
    "/v1/projects": {
      "get": {
        "operationId": "listProjects",
        "parameters": [
          {
            "description": "How many items to return (1–100, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A cursor from a previous response's `next_cursor`.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      },
                      "type": "array"
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "object": {
                      "const": "list",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "List projects",
        "tags": [
          "Projects"
        ]
      },
      "post": {
        "description": "A project is a cost centre inside the account: agents, numbers, knowledge bases and campaigns belong to one, and every call they take is billed there. The account keeps one balance and one invoice — a project is what that balance is being spent on.\n\nOnly `name` is required; the handle is derived from it. Writing a project costs `billing:write` rather than `agents:write`, because `budget_tokens` can stop production traffic and raising it raises the ceiling on what the account can spend.",
        "operationId": "createProject",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create a project",
        "tags": [
          "Projects"
        ]
      }
    },
    "/v1/projects/{id}": {
      "delete": {
        "description": "Refused with 409 while anything still belongs to the project, and always for the account's default. Cascading would delete a customer's agents because they tidied up a label, and orphaning would leave resources pointing at an id that resolves to nothing.\n\nArchiving is what the common case wants: it stops new work and keeps every ledger line that explains what the project cost. Set `status` to `archived`.\n\nTo delete the project that *is* the default, PATCH another project with `default: true` first. The marker moves; the spending stays where it was.",
        "operationId": "deleteProject",
        "parameters": [
          {
            "description": "The project ID or slug.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedObject"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete a project",
        "tags": [
          "Projects"
        ]
      },
      "get": {
        "description": "Addressable by id or by slug, because a project is the one resource a person types into a URL and `support` is what they know it as.",
        "operationId": "getProject",
        "parameters": [
          {
            "description": "The project ID or slug.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve a project",
        "tags": [
          "Projects"
        ]
      },
      "patch": {
        "description": "Only the fields present in the request are changed; omitted fields keep their current value.",
        "operationId": "updateProject",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update a project",
        "tags": [
          "Projects"
        ]
      }
    },
    "/v1/realtime/tokens": {
      "post": {
        "description": "Creates a `web` call and an ephemeral credential that may open that one call's realtime socket and nothing else. This is how a browser or a mobile app joins a conversation: your server authenticates your user, calls this with your API key, and returns the response — the `url` field already carries the credential.\n\nNever ship an API key to a page. This token names one call, carries `calls:write` alone, expires in minutes, and is refused on every path but `GET /v1/realtime`.",
        "operationId": "createRealtimeToken",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RealtimeTokenInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RealtimeToken"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Mint a browser token",
        "tags": [
          "Calls"
        ]
      }
    },
    "/v1/webhook_endpoints": {
      "get": {
        "operationId": "listWebhookEndpoints",
        "parameters": [
          {
            "description": "How many items to return (1–100, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A cursor from a previous response's `next_cursor`.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      },
                      "type": "array"
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "object": {
                      "const": "list",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "List webhook endpoints",
        "tags": [
          "Webhooks"
        ]
      },
      "post": {
        "description": "The signing `secret` is returned only in this response. Store it — it is not readable afterwards.",
        "operationId": "createWebhookEndpoint",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create a webhook endpoint",
        "tags": [
          "Webhooks"
        ]
      },
      "put": {
        "description": "Creates or updates the endpoint for a URL, idempotently — so a service can declare its receiver on every boot instead of somebody running `POST` once by hand and pasting the generated secret into an environment.\n\nPass `secret` and the endpoint signs with the value you already hold, which is what makes repeating this call harmless. Omit it and one is generated and returned, but only on the call that created the endpoint.\n\nReturns 201 when it created the endpoint and 200 when it updated one.",
        "operationId": "putWebhookEndpoint",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "description": "Success."
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "Declare a webhook endpoint",
        "tags": [
          "Webhooks"
        ]
      }
    },
    "/v1/webhook_endpoints/{id}": {
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedObject"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete a webhook endpoint",
        "tags": [
          "Webhooks"
        ]
      },
      "get": {
        "operationId": "getWebhookEndpoint",
        "parameters": [
          {
            "description": "The endpoint ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve a webhook endpoint",
        "tags": [
          "Webhooks"
        ]
      },
      "patch": {
        "description": "Only the fields present in the request are changed; omitted fields keep their current value.",
        "operationId": "updateWebhookEndpoint",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update a webhook endpoint",
        "tags": [
          "Webhooks"
        ]
      }
    },
    "/v1/webhook_endpoints/{id}/deliveries": {
      "get": {
        "description": "Every attempt with its status code and error — the answer to \"did you send it?\".",
        "operationId": "listWebhookDeliveries",
        "parameters": [
          {
            "description": "The endpoint ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List delivery attempts",
        "tags": [
          "Webhooks"
        ]
      }
    },
    "/v1/webhook_endpoints/{id}/test": {
      "post": {
        "description": "Delivers a synthetic `webhook.test` event synchronously and reports the status code and error, so a receiver can be debugged without triggering a real call.",
        "operationId": "testWebhookEndpoint",
        "parameters": [
          {
            "description": "The endpoint ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResult"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Send a test event",
        "tags": [
          "Webhooks"
        ]
      }
    },
    "/v1/workflows": {
      "get": {
        "operationId": "listWorkflows",
        "parameters": [
          {
            "description": "How many items to return (1–100, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A cursor from a previous response's `next_cursor`.",
            "in": "query",
            "name": "starting_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/Workflow"
                      },
                      "type": "array"
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "object": {
                      "const": "list",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        },
        "summary": "List workflows",
        "tags": [
          "Workflows"
        ]
      },
      "post": {
        "description": "Steps are validated against the engine at save time, so an unknown step type fails here rather than mid-call.",
        "operationId": "createWorkflow",
        "parameters": [
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create a workflow",
        "tags": [
          "Workflows"
        ]
      }
    },
    "/v1/workflows/{id}": {
      "delete": {
        "operationId": "deleteWorkflow",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedObject"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete a workflow",
        "tags": [
          "Workflows"
        ]
      },
      "get": {
        "operationId": "getWorkflow",
        "parameters": [
          {
            "description": "The workflow ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Retrieve a workflow",
        "tags": [
          "Workflows"
        ]
      },
      "patch": {
        "description": "Only the fields present in the request are changed; omitted fields keep their current value.",
        "operationId": "updateWorkflow",
        "parameters": [
          {
            "description": "The resource ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update a workflow",
        "tags": [
          "Workflows"
        ]
      }
    },
    "/v1/workflows/{id}/runs": {
      "get": {
        "operationId": "listWorkflowRuns",
        "parameters": [
          {
            "description": "The workflow ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowRunList"
                }
              }
            },
            "description": "Success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List workflow runs",
        "tags": [
          "Workflows"
        ]
      },
      "post": {
        "description": "Runs the workflow synchronously and returns every step's outcome. A workflow that fails still returns 201 — the request succeeded, and the body reports the business outcome.",
        "operationId": "runWorkflow",
        "parameters": [
          {
            "description": "The workflow ID.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Retrying with the same key returns the original response instead of repeating the side effect.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunWorkflowInput"
              }
            }
          },
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowRun"
                }
              }
            },
            "description": "Created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Run a workflow",
        "tags": [
          "Workflows"
        ]
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "servers": [
    {
      "description": "This deployment",
      "url": "https://voice.sphoro.com"
    }
  ],
  "tags": [
    {
      "description": "Create and configure voice agents.",
      "name": "Agents"
    },
    {
      "description": "Defaults shared by a set of agents, resolved when a call loads one.",
      "name": "Agent groups"
    },
    {
      "description": "Ingest documents and search them the way an agent does.",
      "name": "Knowledge bases"
    },
    {
      "description": "Start calls, read transcripts, stream events.",
      "name": "Calls"
    },
    {
      "description": "Declarative multi-step automation triggered by an agent or the API.",
      "name": "Workflows"
    },
    {
      "description": "Tools an agent may call.",
      "name": "Functions"
    },
    {
      "description": "Subscribe an HTTPS endpoint to platform events.",
      "name": "Webhooks"
    },
    {
      "description": "Usage, latency, cost, and CSAT rollups.",
      "name": "Analytics"
    }
  ]
}
