{
  "openapi": "3.1.0",
  "info": {
    "title": "Speko API",
    "version": "1.0.0",
    "description": "Public REST API for the Speko voice gateway. Routes, transcribes, synthesizes, and completes via the highest-scoring provider for your routing intent, with transparent server-side failover.",
    "contact": {
      "email": "abat@speko.ai"
    }
  },
  "servers": [
    {
      "url": "https://api.speko.dev",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/sessions": {
      "post": {
        "summary": "Create a real-time voice session",
        "description": "Creates either a provider-direct S2S session or a LiveKit-backed cascade session. S2S returns a scoped, short-lived provider credential so the client connects directly to OpenAI Realtime, Gemini Live, or xAI Grok Voice; Speko remains in the control and billing path but never proxies media. Cascade returns browser-safe LiveKit credentials and dispatches an STT → LLM → TTS worker.",
        "operationId": "createSession",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Required when the session resolves to `s2s`. Reuse the same value after an ambiguous timeout to recover the same reservation."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSessionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSessionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Insufficient credit balance for the requested S2S entitlement."
          },
          "403": {
            "description": "Provider-direct S2S is not enabled for the organization."
          },
          "409": {
            "description": "The idempotency key is already in use by a different S2S bootstrap."
          },
          "422": {
            "description": "The requested S2S provider, model, voice, sample rate, or route is unavailable."
          },
          "429": {
            "description": "Realtime session capacity is currently exhausted."
          },
          "500": {
            "$ref": "#/components/responses/SessionCreateFailed"
          },
          "502": {
            "description": "The provider-direct control plane returned an invalid session plan."
          },
          "503": {
            "description": "Provider-direct S2S bootstrap is temporarily unavailable."
          }
        }
      },
      "get": {
        "summary": "List voice sessions",
        "description": "Returns sessions visible to the calling key, newest first. Supports cursor pagination and filtering by `status`, `outcome`, `kind`, `agent`, time range, phone, search text, and customer metadata. All filters combine via AND.",
        "operationId": "listSessions",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from `nextCursor` of the previous page. Omit on the first page. Sessions that share a creation time are never skipped between pages. An ISO 8601 timestamp, the format earlier responses returned, is still accepted."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Max number of entries to return."
          },
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by session lifecycle status (e.g. `active`, `ended`, `failed`)."
          },
          {
            "in": "query",
            "name": "outcome",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "completed",
                "no_answer",
                "voicemail",
                "caller_abandoned",
                "busy",
                "declined",
                "invalid_number",
                "carrier_rejected",
                "not_connected",
                "failed"
              ]
            },
            "description": "Filter finished sessions by outcome, derived from `status` and `endReason`. `no_answer` (`sip_no_answer`), `voicemail` (`voicemail_reached`) and `caller_abandoned` are `ended` sessions; `busy` (`sip_busy`), `declined` (`sip_declined`), `invalid_number` (`sip_invalid_number`), `carrier_rejected` (`sip_carrier_rejected`) and `not_connected` (`sip_dial_rejected`) are `failed` ones. `completed` and `failed` match only the ended and failed sessions none of those claim. Combines with `status` via AND."
          },
          {
            "in": "query",
            "name": "kind",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "cascade",
                "s2s",
                "realtime"
              ]
            },
            "description": "Filter by session kind: `cascade` (STT -> LLM -> TTS) or `s2s` (speech-to-speech, also called realtime). `realtime` is accepted as an alias for `s2s`. Any other value is rejected with 400."
          },
          {
            "in": "query",
            "name": "minDuration",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Inclusive minimum displayed session duration in whole seconds. Combined with other filters before pagination. Open sessions with no final duration are excluded. Rejected dials have duration zero."
          },
          {
            "in": "query",
            "name": "maxDuration",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Inclusive maximum displayed session duration in whole seconds. Must be at least minDuration when both are supplied."
          },
          {
            "in": "query",
            "name": "agent",
            "required": false,
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "minItems": 1,
              "maxItems": 100,
              "items": {
                "type": "string",
                "minLength": 1
              }
            },
            "description": "Filter to sessions started against any selected agent. Repeat the parameter for multiple agents, for example `agent=one&agent=two`. Matches `pipelineConfig.agentId` and combines with other filters via AND. Single-agent URLs remain supported."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only sessions created at or after this time (ISO 8601 with offset)."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only sessions created at or before this time (ISO 8601 with offset)."
          },
          {
            "in": "query",
            "name": "phone",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "description": "`true` keeps phone calls only; `false` keeps sessions with no phone leg."
          },
          {
            "in": "query",
            "name": "phoneNumberId",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "description": "Only sessions on this workspace phone number (the id from `GET /v1/phone-numbers`)."
          },
          {
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 200
            },
            "description": "Case-insensitive search text. Each entry then carries a `match` that says where it matched."
          },
          {
            "in": "query",
            "name": "qScope",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "transcript",
                "agent",
                "metadata"
              ],
              "default": "all"
            },
            "description": "Where `q` searches: transcripts, agent names, session metadata, or all three."
          },
          {
            "in": "query",
            "name": "gate",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "readiness",
                "all"
              ],
              "default": "all"
            },
            "description": "`live` keeps real traffic only; `readiness` keeps production-readiness (gate) test runs only."
          },
          {
            "in": "query",
            "name": "metadata",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "maxProperties": 10,
              "additionalProperties": {
                "type": "string",
                "maxLength": 512
              }
            },
            "description": "Exact match on the `metadata` you sent when creating the session, written `metadata[key]=value`, for example `metadata[leadId]=L-42&metadata[campaign]=spring`. Every pair must match (AND). Values compare as strings, so a stored number or boolean does not match. Up to 10 keys of at most 128 characters. Narrow wide scans with `from` and `to`."
          }
        ],
        "responses": {
          "200": {
            "description": "Page of session entries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionEntries"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/sessions/phone": {
      "post": {
        "summary": "Place an outbound phone call",
        "description": "Creates a voice session, dispatches the configured agent worker, and dials the destination over LiveKit SIP. Supply either `agentId` or `intent`.",
        "operationId": "createPhoneSession",
        "tags": [
          "Telephony"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoiceDialRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Call dialing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceDialResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before dialing. Codes: `VALIDATION_ERROR` (with `issues`), `MISSING_AGENT_OR_INTENT`, `AGENT_REQUIRED`, `MISSING_TEMPLATE_VARIABLES`, `PIPELINE_CONFIG_TOO_LARGE` (with `bytes` and `limit`), `RING_TIMEOUT_OUT_OF_RANGE`, `DTMF_IN_PHONE_NUMBER`, `WEBHOOK_TAGS_REQUIRE_AGENT`, `PIPECAT_CALL_OVERRIDE_UNSUPPORTED`, `VOICE_PROVIDER_NOT_ALLOWED`, `INVALID_PROVIDER_OPTIONS`, `PARAMS_UNSUPPORTED_ON_PINNED_TRANSPORT`, `S2S_VOICE_UNSUPPORTED`, `NO_FROM_NUMBER`, `CALLER_ID_NOT_FOUND`, `CALLER_ID_NOT_OUTBOUND`, `CALLER_ID_SUSPENDED`, `SIP_TRUNK_NOT_CONFIGURED`, `AGENT_VERSION_REQUIRES_AGENT`, `AGENT_VERSION_CONFLICT` (`agentVersion` with `useDraft`), `IDEMPOTENCY_KEY_INVALID`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "VALIDATION_ERROR": {
                    "value": {
                      "error": "Invalid request",
                      "code": "VALIDATION_ERROR",
                      "issues": [
                        {
                          "path": "to",
                          "message": "Required"
                        }
                      ]
                    }
                  },
                  "PIPELINE_CONFIG_TOO_LARGE": {
                    "value": {
                      "error": "pipeline_config_too_large",
                      "code": "PIPELINE_CONFIG_TOO_LARGE",
                      "bytes": 70211,
                      "limit": 65536
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Billing refused the call. `INSUFFICIENT_CREDITS`: the balance cannot cover the call. `SPENDING_CAP_REACHED`: the organization hit its spending cap. `PHONE_NUMBER_CARD_REQUIRED` and `PHONE_NUMBER_PROVISIONING_CREDITS_REQUIRED`: a caller ID has to be provisioned first and needs a card or credits. `DESTINATION_REQUIRES_CARD`: the destination country needs a card on file; `billingUrl` points to billing settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "INSUFFICIENT_CREDITS": {
                    "value": {
                      "error": "Insufficient credit balance for this call. Calls cost $0.10/min. Add credits or turn on auto top-up.",
                      "code": "INSUFFICIENT_CREDITS"
                    }
                  },
                  "SPENDING_CAP_REACHED": {
                    "value": {
                      "error": "Monthly spending cap reached. Raise or remove the cap to start new sessions and calls before it resets.",
                      "code": "SPENDING_CAP_REACHED",
                      "capUsd": 500,
                      "spentUsd": 500.12,
                      "resetsAt": "2026-11-01T00:00:00.000Z",
                      "currency": "USD"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The call is not allowed. `DESTINATION_NOT_ALLOWED`: the destination is under sanctions. `PHONE_NUMBER_CONSENT_REQUIRED`, `PHONE_NUMBER_SCOPE_REQUIRED`, `PHONE_NUMBER_KYB_ACCESS_SUSPENDED`: provisioning a caller ID needs consent, a key with phone-number scope, or a completed business verification. `PHONE_NUMBER_KYB_REQUIRED` and `PHONE_NUMBER_KYB_SUSPENDED`: outbound dialing needs a completed business verification (or it was suspended); `requiredAttestation` names the attestation to submit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "DESTINATION_NOT_ALLOWED": {
                    "value": {
                      "error": "Calls to +53 (Cuba) are not allowed: the destination is under sanctions.",
                      "code": "DESTINATION_NOT_ALLOWED"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The agent, or the pinned `agentVersion`, was not found (`AGENT_NOT_FOUND`, `AGENT_VERSION_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`CALL_ALREADY_DIALING`: a call from this workspace to this number is still being placed on a new caller ID. Wait for it to finish. `NO_FROM_NUMBER`: a legacy shared caller ID has no dedicated replacement. Idempotency-Key conflicts: `IDEMPOTENCY_KEY_IN_PROGRESS`, `IDEMPOTENCY_KEY_REUSED`, `IDEMPOTENCY_KEY_OUTCOME_UNKNOWN`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "CALL_ALREADY_DIALING": {
                    "value": {
                      "error": "A call from this workspace to this number is already being placed. Wait for it to finish before you try again.",
                      "code": "CALL_ALREADY_DIALING"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`NO_CAPABLE_PROVIDER`: no provider can serve the requested pipeline. `param` names the stage.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "NO_CAPABLE_PROVIDER": {
                    "value": {
                      "error": "no_capable_provider",
                      "code": "NO_CAPABLE_PROVIDER",
                      "param": "tts"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "An upstream step failed after the request was accepted. Codes: `LIVEKIT_SIP_DIAL_FAILED`, `TELNYX_DIAL_FAILED`, `AGENT_DISPATCH_FAILED`, `PRE_CALL_WEBHOOK_FAILED`, `PHONE_NUMBER_PROVISIONING_FAILED`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "LIVEKIT_SIP_DIAL_FAILED": {
                    "value": {
                      "error": "LiveKit SIP outbound dial failed",
                      "code": "LIVEKIT_SIP_DIAL_FAILED"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "A dependency is not ready. `PHONE_NUMBER_PROVISIONING_PENDING`: a caller ID could not be provisioned right now; retry shortly. `PIPECAT_UNAVAILABLE` and `TELNYX_CALL_CONTROL_NOT_CONFIGURED`: the runtime this call needs is not configured.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "PHONE_NUMBER_PROVISIONING_PENDING": {
                    "value": {
                      "error": "Phone number provisioning is temporarily unavailable.",
                      "code": "PHONE_NUMBER_PROVISIONING_PENDING"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            "description": "Makes a retry safe. A repeat of the same request with the same key, from the same organization and credential within 24 hours, returns the first call's response (with `X-Idempotent-Replay: true`) and places no second call. While the first request is still running, a repeat gets 409 `IDEMPOTENCY_KEY_IN_PROGRESS`; retry shortly. A different body under a used key gets 409 `IDEMPOTENCY_KEY_REUSED`. A 4xx response frees its key, so a corrected retry can reuse it. If the first request ended in a 5xx or never finished, the key answers 409 `IDEMPOTENCY_KEY_OUTCOME_UNKNOWN`: check `GET /v1/sessions` before dialing again with a new key. Without the header, every request places a call."
          }
        ]
      }
    },
    "/v1/transcribe": {
      "post": {
        "summary": "Transcribe audio (speech to text)",
        "description": "Routes the request to the best STT provider for your `(language, region, optimizeFor)` intent, with automatic failover to runner-up providers before the first event is emitted. Body is binary audio. Routing intent goes in the `x-speko-intent` header (JSON). The response is a Server-Sent Events stream: `meta`, `transcript`, `done`, and `error`.",
        "operationId": "transcribe",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "x-speko-intent",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "JSON-encoded `RoutingIntent`. Example: `{\"language\":\"en-US\",\"region\":\"global\"}`."
          },
          {
            "in": "header",
            "name": "x-speko-constraints",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional JSON-encoded `PipelineConstraints`. Each `allowedProviders` entry is either `\"<vendor>\"` (any model from that vendor) or `\"<vendor>:<model>\"` (a specific model). Example: `{\"allowedProviders\":{\"stt\":[\"deepgram:nova-3\"]}}`."
          },
          {
            "in": "header",
            "name": "x-speko-stt-options",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional JSON-encoded STT options, max 4 KB. Fields: `keywords` (string[], <=200, vocabulary bias), `language` (BCP-47 or `auto`, provider-level override that does not change routing), `workload` (`conversation` | `transcription` | `narration`, applies registry defaults per provider), `diarization` (batch-only boolean, demote-class: incapable providers are excluded from failover; 422 `no_capable_provider` when none remain), `speakersExpected` (integer hint), `smartFormat` (boolean), `fillerWords` (boolean), `profanityFilter` (boolean), `wordTimestamps` (batch-only boolean, demote-class; the `done` frame carries `words: [{text, start, end}]` in seconds — served by `gemini-3.5-transcribe`, which must be pinned through `X-Speko-Constraints` or the request answers 422 `no_capable_provider`, and which cannot combine it with `keywords`), `providerOptions` (per-provider params validated against the registry, e.g. `{\"deepgram\":{\"dictation\":true}}`; apply only when that provider serves the request and never cross providers on failover). Example: `{\"workload\":\"transcription\",\"diarization\":true}`."
          },
          {
            "in": "header",
            "name": "Content-Type",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Audio MIME type, e.g. `audio/wav`, `audio/mpeg`, `audio/pcm;rate=16000`."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Binary audio. WAV, MP3, or raw PCM accepted.",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "audio/wav": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "audio/mpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transcript event stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE events: `meta` with provider/model, zero or more `transcript` events, final `done` containing a `TranscribeResponse`, or `error` after streaming has started."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidAudio"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/TranscribeFailed"
          }
        }
      }
    },
    "/v1/voices": {
      "get": {
        "summary": "List TTS voices",
        "description": "Returns the curated catalog of TTS voices grouped by provider, plus the list of TTS providers Speko can route for YOUR organization. Providers are filtered by key coverage: one appears only when your organization has its own credential for it (`source: \"BYOK\"`) or Speko holds a managed key (`source: \"MANAGED\"`). A provider with neither is omitted, and its voices are omitted with it — so a voice returned here is always one a session can actually be created with. ElevenLabs is included in `providers` with `voicesFetchedLive: true`; its voices are not in `voices`. Find them with `GET /v1/tts/voices?vendor=elevenlabs`, which works on a Speko-managed key.\n\nThis endpoint lists providers and a curated voice roster. Its only filter is `provider`; other query parameters such as `language` or `gender` are ignored. For filtered, paginated voice search by language, gender, accent, use case, or quality, use `GET /v1/tts/voices`.",
        "operationId": "listVoices",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter to a single provider's voices. Accepts either the routing key (`cartesia`, `xai`, `alibaba`, `openai`, `inworld`, `elevenlabs`) or the catalog suffix form (`xai-tts`, `alibaba-tts`, `openai-tts`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Voice catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoicesListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/synthesize": {
      "post": {
        "summary": "Synthesize speech (text to speech)",
        "description": "Routes the request to the best TTS provider for your intent, with automatic failover before the first audio chunk. Returns chunked binary audio. The `Content-Type` header (mirrored in `X-Speko-Audio-Format`) tells you the format.",
        "operationId": "synthesize",
        "tags": [
          "Voice"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SynthesizeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chunked audio bytes",
            "headers": {
              "Content-Type": {
                "schema": {
                  "type": "string"
                },
                "description": "Audio MIME type, e.g. `audio/pcm;rate=24000` or `audio/mpeg`."
              },
              "X-Speko-Audio-Format": {
                "schema": {
                  "type": "string"
                },
                "description": "Mirrors `Content-Type` for client-side dispatch."
              },
              "X-Speko-Provider": {
                "schema": {
                  "type": "string"
                },
                "description": "Provider that actually handled the request."
              },
              "X-Speko-Model": {
                "schema": {
                  "type": "string"
                },
                "description": "Specific model used."
              },
              "X-Speko-Failover-Count": {
                "schema": {
                  "type": "integer"
                },
                "description": "Number of providers tried before success."
              },
              "X-Speko-First-Byte-Ms": {
                "schema": {
                  "type": "integer"
                },
                "description": "Server-side time to first audio from the WINNING provider only (its synthesize call to its first audio chunk). Excludes failed failover attempts, so it is lower than the time a client waits."
              },
              "X-Speko-Request-First-Byte-Ms": {
                "schema": {
                  "type": "integer"
                },
                "description": "Server-side time to first audio for the whole request: gateway entry to first audio chunk, including routing, credit gating and every failed failover attempt. Use this one for client timeout budgeting."
              },
              "X-Speko-Scores-Run-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Benchmark snapshot id used for routing."
              },
              "X-Speko-Applied-Params": {
                "schema": {
                  "type": "string"
                },
                "description": "JSON array of param names applied to the winning provider. Present only when the request used `workload` or `providerOptions`."
              },
              "X-Speko-Warnings": {
                "schema": {
                  "type": "string"
                },
                "description": "JSON array of param warnings (`param_dropped`, `out_of_range`, `provider_not_pinned`), capped at 2 KB. Present only when non-empty."
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "description": "The request is valid, but no provider can serve it. `TEXT_TOO_LONG`: every candidate refused the text length before any provider call (over the provider's per-request limit, or too long to finish a `delivery: \"link\"` synthesis inside the request time limit); nothing is billed, and the body carries `textLength` and `maxChars`. Split the text into parts of at most `maxChars` characters. `NO_PROVIDER_AVAILABLE`: no provider serves this language, sample rate or pin. `ALL_PROVIDERS_FAILED`: every candidate refused the request with a 4xx.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Human-readable message."
                    },
                    "code": {
                      "type": "string",
                      "description": "Machine-readable code, for example `TEXT_TOO_LONG`."
                    },
                    "textLength": {
                      "type": "integer",
                      "description": "`TEXT_TOO_LONG` only. The text length in Unicode code points, after spoken-form rewriting."
                    },
                    "maxChars": {
                      "type": "integer",
                      "description": "`TEXT_TOO_LONG` only. A part length, in Unicode code points, that a candidate for this request takes in one request. Split the text into parts of at most this length."
                    }
                  }
                },
                "examples": {
                  "textTooLong": {
                    "value": {
                      "error": "Text is 8000 characters, and no provider for this request can synthesize it in one request. Split the text into parts of at most 5000 characters and send each part as its own request.",
                      "code": "TEXT_TOO_LONG",
                      "textLength": 8000,
                      "maxChars": 5000
                    }
                  }
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/SynthesizeFailed"
          }
        }
      }
    },
    "/v1/complete": {
      "post": {
        "summary": "Generate an LLM completion",
        "description": "Single-turn LLM call routed to the best provider for your intent, with automatic failover before the first event is emitted. The response is a Server-Sent Events stream: `meta`, `delta`, `tool_call`, `server_tool_call`, `done`, and `error`.",
        "operationId": "complete",
        "tags": [
          "Voice"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompleteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Completion event stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE events: `meta`, zero or more `delta` and tool events, final `done` containing a `CompleteResponse`, or `error` after streaming has started."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/CompleteFailed"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "summary": "List webhook endpoints",
        "description": "Lists active organization-level lifecycle webhook endpoints. Secret values are never returned.",
        "operationId": "listWebhookEndpoints",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Active webhook endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "summary": "Create a webhook endpoint",
        "description": "Creates an organization endpoint with event, agent, and optional tag routing. Plaintext signing and auth-header secrets are write-only.",
        "operationId": "createWebhookEndpoint",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Endpoint created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "The endpoint conflicts with an existing extraction definition or pre-call route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Get a webhook endpoint",
        "operationId": "getWebhookEndpoint",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Webhook endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Endpoint not found."
          }
        }
      },
      "patch": {
        "summary": "Update a webhook endpoint",
        "description": "Updates supplied endpoint fields. Omit an existing secret auth-header value to retain it.",
        "operationId": "updateWebhookEndpoint",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Endpoint not found."
          },
          "409": {
            "description": "Routing or extraction conflict."
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook endpoint",
        "description": "Soft-deletes the endpoint and cancels pending retries. Existing delivery history remains until its 30-day expiry.",
        "operationId": "deleteWebhookEndpoint",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "204": {
            "description": "Endpoint deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Endpoint not found."
          }
        }
      }
    },
    "/v1/webhook-deliveries": {
      "get": {
        "summary": "List webhook deliveries",
        "description": "Returns one row per endpoint delivery, newest first. Bodies and logs expire after 30 days.",
        "operationId": "listWebhookDeliveries",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "endpointId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "event",
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          {
            "in": "query",
            "name": "agentId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "$ref": "#/components/schemas/WebhookDeliveryStatus"
            }
          },
          {
            "in": "query",
            "name": "sessionId",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "in": "query",
            "name": "eventId",
            "schema": {
              "type": "string"
            },
            "description": "Speko lifecycle webhook UUID or stable provider event ID."
          },
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a previous response."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/webhook-deliveries/{id}": {
      "get": {
        "summary": "Get webhook delivery details",
        "description": "Returns the stored endpoint-specific payload and chronological sanitized attempt history.",
        "operationId": "getWebhookDelivery",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Delivery not found."
          }
        }
      }
    },
    "/v1/webhook-deliveries/redeliver": {
      "post": {
        "summary": "Redeliver webhooks in a time range",
        "description": "Adds a manual attempt to every delivery created between `from` and `to` that matches the filters, oldest first, up to `limit` per call. Each attempt follows the single-delivery rules: the original payload and webhook ID, the endpoint's current URL and credentials. Pre-call, expired, deleted-endpoint and iMessage deliveries are never selected. When `truncated` is true, call again with `cursor` set to `nextCursor` to continue; the cursor moves past every row already attempted, so rows that fail again are not repeated.",
        "operationId": "redeliverWebhooksInRange",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkWebhookRedeliveryRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The attempts made in this call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkWebhookRedeliveryResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The credential lacks the `speko:write` scope."
          }
        }
      }
    },
    "/v1/webhook-deliveries/{id}/redeliver": {
      "post": {
        "summary": "Redeliver a webhook",
        "description": "Adds a manual attempt using the original event payload and webhook ID with the endpoint's current URL and credentials. Pre-call, expired, and provider-retried iMessage deliveries cannot be manually redelivered.",
        "operationId": "redeliverWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Manual delivery result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookRedeliveryResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Delivery not found."
          },
          "409": {
            "description": "Delivery is expired, pre-call, provider-retried, or belongs to a deleted endpoint."
          }
        }
      }
    },
    "/v1/agents": {
      "get": {
        "summary": "List agents",
        "description": "Returns every agent belonging to the authenticated organization. Agents are scoped per-org — IDs from another org will never appear here.",
        "operationId": "listAgents",
        "tags": [
          "Agents"
        ],
        "responses": {
          "200": {
            "description": "Agents owned by the authenticated organization.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Agent"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "summary": "Create an agent",
        "description": "Persists a reusable voice persona — `systemPrompt`, optional `voice`, routing `intent`, and optional `llmOptions` — keyed by `name` within the authenticated organization. Pass the returned `id` as `agentId` on `POST /v1/sessions` to seed a session from this agent.",
        "operationId": "createAgent",
        "tags": [
          "Agents"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAgentRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Agent created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/agents/{id}": {
      "get": {
        "summary": "Get an agent",
        "description": "Fetch a single agent by id. Scoped to the authenticated organization — agents owned by another org always return 404.",
        "operationId": "getAgent",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent id, prefixed `agent_`."
          }
        ],
        "responses": {
          "200": {
            "description": "Agent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/AgentNotFound"
          }
        }
      },
      "patch": {
        "summary": "Update an agent",
        "description": "Partial update — every field on the body is optional and only supplied keys are written.",
        "operationId": "updateAgent",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent id, prefixed `agent_`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAgentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Agent updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/AgentNotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete an agent",
        "description": "Removes the agent and its registered tools. The organization's only remaining agent cannot be deleted and returns 409.",
        "operationId": "deleteAgent",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent id, prefixed `agent_`."
          }
        ],
        "responses": {
          "200": {
            "description": "Agent deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "deleted"
                  ],
                  "properties": {
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/AgentNotFound"
          },
          "409": {
            "$ref": "#/components/responses/AgentDeleteConflict"
          }
        }
      }
    },
    "/v1/agents/{id}/calls": {
      "get": {
        "summary": "List calls for an agent",
        "description": "Returns recent calls whose session pipeline config references this agent. Results are newest first and support cursor pagination.",
        "operationId": "listAgentCalls",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent id."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Use `next_cursor` from the previous page."
          },
          {
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only include calls created at or after this timestamp."
          }
        ],
        "responses": {
          "200": {
            "description": "Page of calls",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentCallListPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/AgentNotFound"
          }
        }
      }
    },
    "/v1/agents/{id}/analysis": {
      "get": {
        "summary": "Post-call analysis for an agent",
        "description": "One row per call, with the values the post-call analysis extracted for each declared field. The schema is the same merge the analysis itself uses — the agent's own `extractionFields` plus any declared on the webhook endpoints that resolve for this agent, agent-level winning a name collision — and is returned alongside the rows so a client can render columns without a second request.\n\nOffset-paginated rather than cursor-paginated: a cursor cannot express a position in an ordering by an arbitrary extracted column. Capped at 10,000 rows: a `page` whose first row would fall past the cap returns an empty page with `has_more: false`.",
        "operationId": "getAgentAnalysis",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent id."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "in": "query",
            "name": "sort",
            "required": false,
            "schema": {
              "type": "string",
              "default": "created_at"
            },
            "description": "`created_at`, or the name of an extraction field the agent declares. Any other value is a 400 — an ordering we cannot produce is never silently ignored."
          },
          {
            "in": "query",
            "name": "dir",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "in": "query",
            "name": "filter[FIELD]",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by one declared field, typed by its declaration: exact match for `number`, `boolean` and `enum`; substring match for `string`. A value that cannot be read as the declared type is a 400."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "gate",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "readiness",
                "all"
              ],
              "default": "live"
            },
            "description": "Production-readiness gate runs are excluded by default — they are not real calls."
          }
        ],
        "responses": {
          "200": {
            "description": "Page of analysed calls",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentAnalysisPage"
                }
              }
            }
          },
          "400": {
            "description": "Unknown sort field, or a filter value that does not match its declared type."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/AgentNotFound"
          }
        }
      }
    },
    "/v1/agents/{id}/analysis.csv": {
      "get": {
        "summary": "Export an agent's post-call analysis as CSV",
        "description": "The same rows as `GET /v1/agents/{id}/analysis`, with the same sort, filters and merged schema, but unpaginated (capped at 10,000 rows). Header columns follow the declared schema order, so two exports of one agent line up column for column even when a call left a field empty.",
        "operationId": "exportAgentAnalysisCsv",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent id."
          },
          {
            "in": "query",
            "name": "sort",
            "required": false,
            "schema": {
              "type": "string",
              "default": "created_at"
            }
          },
          {
            "in": "query",
            "name": "dir",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "in": "query",
            "name": "filter[FIELD]",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "gate",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "readiness",
                "all"
              ],
              "default": "live"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Unknown sort field, or a filter value that does not match its declared type."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/AgentNotFound"
          }
        }
      }
    },
    "/v1/providers/known": {
      "get": {
        "summary": "List known providers",
        "description": "Returns every (vendor, model) pair the router knows about per pipeline layer, plus one vendor-wildcard row per vendor. The `id` field on each entry is the verbatim string callers put into `allowedProviders` on session create, transcribe, synthesize, complete, or `stackPreferences` on an agent. Use this to populate UI pickers or validate user-supplied allowlists.",
        "operationId": "getKnownProviders",
        "tags": [
          "Providers"
        ],
        "responses": {
          "200": {
            "description": "Per-modality lists of allowlist-eligible entries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnownProviders"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/phone-numbers": {
      "get": {
        "summary": "List phone numbers",
        "description": "Returns managed and customer SIP-trunk phone numbers registered to the authenticated organization.",
        "operationId": "listPhoneNumbers",
        "tags": [
          "Telephony"
        ],
        "responses": {
          "200": {
            "description": "Organization phone numbers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PhoneNumber"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "summary": "Buy a managed phone number",
        "description": "Orders a platform-managed phone number and registers it for inbound and/or outbound calling. A current business declaration, no compliance hold, the global purchase switch, and sufficient credits are required. The number's current carrier price is read before any charge. Each number costs, per component, $1 setup and $2 a month or the carrier's own price, whichever is higher, with no markup. A number priced above that standard price is bought only when `expectedUpfrontCostUsd` and `expectedMonthlyCostUsd` are at or above the current quote; the quote is what is charged, and the monthly rental renews at it.",
        "operationId": "createPhoneNumber",
        "tags": [
          "Telephony"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Phone number",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`PHONE_NUMBER_PRICE_REQUIRES_DASHBOARD`: the number is priced above the standard price and the request came from an MCP or OAuth connector, which can only buy standard-price numbers. The body carries `quote`. Nothing was ordered or charged; the owner can buy it in the dashboard."
          },
          "409": {
            "description": "`PHONE_NUMBER_PRICE_CONFIRMATION_REQUIRED`: the number is priced above the standard price and the request did not confirm at least its current price. The body carries `quote` (`upfrontCostUsd`, `monthlyCostUsd`). Nothing was ordered or charged."
          },
          "422": {
            "description": "`PHONE_NUMBER_PRICE_NOT_SUPPORTED`: Speko cannot sell this number: it is premium-rate, outside the US and Canada, or the carrier sends no price for it. Pick another from `GET /v1/phone-numbers/available`, or import it from your own carrier.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "Speko cannot sell this number: it is premium-rate, outside the US and Canada, or the carrier sends no price for it. Pick another from GET /v1/phone-numbers/available, or import it from your own carrier.",
                      "code": "PHONE_NUMBER_PRICE_NOT_SUPPORTED"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/phone-numbers/available": {
      "get": {
        "summary": "Search available managed numbers",
        "description": "Searches the platform-managed phone-number pool. A current phone-number business declaration, no compliance hold, and the buy-phone-numbers feature flag are required. Each row carries its own price: $1 setup and $2 a month, or the carrier's price when higher (`aboveStandardPrice`).",
        "operationId": "searchAvailablePhoneNumbers",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "country",
            "required": false,
            "description": "`US` or `CA`. When omitted, a Canadian `areaCode` searches Canada and anything else searches the US.",
            "schema": {
              "type": "string",
              "enum": [
                "US",
                "CA"
              ]
            }
          },
          {
            "in": "query",
            "name": "areaCode",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^\\d{3}$"
            }
          },
          {
            "in": "query",
            "name": "numberType",
            "required": false,
            "description": "`local` (the default) or `toll_free`. A toll-free area code such as `833` searches toll-free numbers without it. Toll-free numbers are listed under the US.",
            "schema": {
              "type": "string",
              "enum": [
                "local",
                "toll_free"
              ]
            }
          },
          {
            "in": "query",
            "name": "locality",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "state",
            "required": false,
            "description": "Two-letter US state code (e.g. `TX`), or Canadian province code (e.g. `ON`) with `country=CA`.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Available numbers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AvailablePhoneNumber"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/phone-numbers/import": {
      "post": {
        "summary": "Import a SIP trunk phone number",
        "description": "Registers a customer-owned phone number from an installed SIP connection or a legacy outbound trunk id.",
        "operationId": "importSipTrunkPhoneNumber",
        "tags": [
          "Telephony"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberImportRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Phone number",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/phone-numbers/kyb": {
      "get": {
        "summary": "Get phone-number business verification",
        "description": "Returns the latest business declaration, immutable attestor snapshot, current attestation contract, compliance access state, and optional legacy 10DLC-derived prefill.",
        "operationId": "getPhoneNumberKyb",
        "tags": [
          "Telephony"
        ],
        "responses": {
          "200": {
            "description": "Business verification state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberKybOverview"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/phone-numbers/kyb/draft": {
      "put": {
        "summary": "Save phone-number business verification draft",
        "operationId": "savePhoneNumberKybDraft",
        "tags": [
          "Telephony"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberKybDraftRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberKybSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/phone-numbers/kyb/submit": {
      "post": {
        "summary": "Submit phone-number business verification",
        "operationId": "submitPhoneNumberKyb",
        "tags": [
          "Telephony"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberKybSubmitRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submitted verification",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberKybSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "The displayed attestation version is stale, or the declaration is already approved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "requiredAttestation": {
                      "$ref": "#/components/schemas/PhoneNumberKybAttestationContract"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/phone-numbers/{id}": {
      "get": {
        "summary": "Get a phone number",
        "operationId": "getPhoneNumber",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Phone number",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "patch": {
        "summary": "Update a phone number",
        "operationId": "updatePhoneNumber",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Phone number",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "delete": {
        "summary": "Release a phone number",
        "description": "Releases the number at its carrier and removes it from the workspace. A number with SMS history is retired instead of deleted: its queued and scheduled SMS are canceled, it stops appearing in phone number lists and reads, and its SMS conversations and messages stay readable under the same `phone_number_id`.",
        "operationId": "deletePhoneNumber",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Release result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "released"
                  ],
                  "properties": {
                    "released": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}": {
      "get": {
        "summary": "Get call detail",
        "description": "Returns the call session, transcript, report if available, transfer attempts, and recording metadata.",
        "operationId": "getCall",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Call detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}/events": {
      "get": {
        "summary": "List call events",
        "operationId": "listCallEvents",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Call events",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallEventsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}/report": {
      "get": {
        "summary": "Get call report",
        "operationId": "getCallReport",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Call report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallReport"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}/report/finalize": {
      "post": {
        "summary": "Finalize call report",
        "description": "Runs or re-runs analysis for a call and optionally retries post-call webhook delivery.",
        "operationId": "finalizeCallReport",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FinalizeCallReportRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Finalization result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FinalizeCallReportResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}/recording": {
      "get": {
        "summary": "Get call recording URL",
        "operationId": "getCallRecording",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "A short-lived download URL for the recording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecordingUrl"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No recording to return. `NOT_FOUND`: the id is unknown to your organization. `RECORDING_NOT_AVAILABLE`: the recording is not `ready` (still `pending` or `uploading`, or `failed`, `suppressed`, `discarded`, or absent on provider-direct S2S); the body carries the session's `recordingStatus` as `status`. `RECORDING_DISABLED`: recording is not configured on this server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "RECORDING_NOT_AVAILABLE": {
                    "value": {
                      "error": "recording not available",
                      "code": "RECORDING_NOT_AVAILABLE",
                      "status": "uploading"
                    }
                  },
                  "NOT_FOUND": {
                    "value": {
                      "error": "Session not found",
                      "code": "NOT_FOUND"
                    }
                  },
                  "RECORDING_DISABLED": {
                    "value": {
                      "error": "recording is not configured on this server",
                      "code": "RECORDING_DISABLED"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Minting the signed URL failed. Retry the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "SIGNED_URL_FAILED": {
                    "value": {
                      "error": "failed to mint signed URL",
                      "code": "SIGNED_URL_FAILED"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/calls/{id}/end": {
      "post": {
        "summary": "End a live session now",
        "description": "Kill switch for any live session — browser/WebRTC sessions included (`calls` routes accept any session id). Deletes the room, which drops every participant; the normal close and metering flow runs via the room webhooks. Idempotent: ending an already-ended session returns `status: \"already_ended\"`. Use this from your backend to stop billing for a session your client can no longer reach.",
        "operationId": "endCall",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Session already ended",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "status"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "already_ended"
                      ]
                    },
                    "ended_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Teardown requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "status"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "ending"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Session has no room to tear down"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No session with this id in your organization"
          }
        }
      }
    },
    "/v1/calls/{id}/messages": {
      "post": {
        "summary": "Send a message into a live call",
        "description": "Push an update from your backend into a live call, for results that finish after the tool call that started them returned. The voice model only acts after the caller speaks, so it cannot pick a late result up by itself. In `respond` mode (default) the text is added to the agent's context and, at the next gap in the conversation, the agent tells the caller in its own words; the caller can interrupt and ask follow-ups as usual. In `context` mode the text is only added to the agent's context and nothing is spoken. Delivery is asynchronous: `202` means the message was published to the call, and the outcome appears on the call's events as `worker.agent_message_delivered`. Not available for provider-direct S2S sessions.",
        "operationId": "sendCallMessage",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "additionalProperties": false,
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4000,
                    "description": "The update for the caller."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "respond",
                      "context"
                    ],
                    "default": "respond",
                    "description": "`respond` has the agent tell the caller at the next gap in the conversation; `context` only adds the text to the agent's context."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Message published to the live call",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "status",
                    "message_id",
                    "mode"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "sent"
                      ]
                    },
                    "message_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "respond",
                        "context"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (`VALIDATION_ERROR`)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No session with this id in your organization"
          },
          "409": {
            "description": "The call is not live (`CALL_NOT_LIVE`), or it is a provider-direct S2S session (`MESSAGES_UNSUPPORTED`)"
          }
        }
      }
    },
    "/v1/calls/{id}/transfers/blind": {
      "post": {
        "summary": "Start blind transfer",
        "operationId": "blindTransferCall",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BlindTransferRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Transfer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallTransfer"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}/transfers/warm": {
      "post": {
        "summary": "Start warm transfer",
        "operationId": "warmTransferCall",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WarmTransferRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Transfer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallTransferResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}/transfers/{transferId}/complete": {
      "post": {
        "summary": "Complete warm transfer",
        "operationId": "completeWarmTransfer",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "in": "path",
            "name": "transferId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompleteWarmTransferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallTransfer"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/calls/{id}/transfers/{transferId}/cancel": {
      "post": {
        "summary": "Cancel warm transfer",
        "operationId": "cancelWarmTransfer",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "in": "path",
            "name": "transferId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelWarmTransferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallTransferResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/callbacks": {
      "get": {
        "summary": "List scheduled callbacks",
        "operationId": "listScheduledCallbacks",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/ScheduledCallbackStatus"
            }
          },
          {
            "in": "query",
            "name": "source_session_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scheduled callbacks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledCallbacksResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/callbacks/{id}": {
      "get": {
        "summary": "Get scheduled callback",
        "operationId": "getScheduledCallback",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Scheduled callback",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledCallback"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/callbacks/{id}/cancel": {
      "post": {
        "summary": "Cancel scheduled callback",
        "operationId": "cancelScheduledCallback",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scheduled callback",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledCallback"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/callbacks/{id}/dispatch": {
      "post": {
        "summary": "Dispatch scheduled callback now",
        "operationId": "dispatchScheduledCallback",
        "tags": [
          "Telephony"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Scheduled callback",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledCallback"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/sessions/{id}": {
      "get": {
        "summary": "Get a session",
        "description": "Returns one session: its status and timing, usage and ledger, the facts recorded on the line (answer, ring time, SIP code, keypad presses, language switches, speech detection, machine detection), and the stored post-call report. The same facts arrive on the `call.report` webhook in snake_case.",
        "operationId": "getSession",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Session id."
          }
        ],
        "responses": {
          "200": {
            "description": "The session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Session id is unknown to the calling organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "Session not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{id}/recording": {
      "get": {
        "summary": "Get a signed URL for the session recording",
        "description": "Returns a short-lived download URL for the session recording as JSON: `200` with `{ \"url\": \"...\", \"expiresAt\": \"...\" }`. It does not redirect, so fetch `url` yourself. For sessions on the default runtime the URL is a signed Google Cloud Storage link to the mixed-mono Ogg/Opus file, valid for 5 minutes; call this endpoint again for a fresh one. Recordings are kept for 30 days after the session ends.",
        "operationId": "getSessionRecording",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Session id."
          }
        ],
        "responses": {
          "200": {
            "description": "A short-lived download URL for the recording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecordingUrl"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No recording to return. `NOT_FOUND`: the id is unknown to your organization. `RECORDING_NOT_AVAILABLE`: the recording is not `ready` (still `pending` or `uploading`, or `failed`, `suppressed`, `discarded`, or absent on provider-direct S2S); the body carries the session's `recordingStatus` as `status`. `RECORDING_DISABLED`: recording is not configured on this server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "RECORDING_NOT_AVAILABLE": {
                    "value": {
                      "error": "recording not available",
                      "code": "RECORDING_NOT_AVAILABLE",
                      "status": "uploading"
                    }
                  },
                  "NOT_FOUND": {
                    "value": {
                      "error": "Session not found",
                      "code": "NOT_FOUND"
                    }
                  },
                  "RECORDING_DISABLED": {
                    "value": {
                      "error": "recording is not configured on this server",
                      "code": "RECORDING_DISABLED"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Minting the signed URL failed. Retry the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "SIGNED_URL_FAILED": {
                    "value": {
                      "error": "failed to mint signed URL",
                      "code": "SIGNED_URL_FAILED"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{id}/turns": {
      "post": {
        "summary": "Ingest finalized transcript turns",
        "description": "Append finalized STT and LLM turns to a session's transcript. Worker-only — the agent worker batches and debounces (~200ms) finalized turns and POSTs them here. Interim STT partials are not accepted; only finalized turns are persisted. Idempotent on `(session_id, index)`: re-posting a turn with the same index replaces the existing entry, so retries on transient errors are safe.",
        "operationId": "postSessionTurns",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Session id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostSessionTurnsRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Turns accepted for persistence. Response body is empty."
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Session id is unknown to the calling organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "session not found",
                      "code": "SESSION_NOT_FOUND"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{id}/transcript": {
      "get": {
        "summary": "Get a session's full transcript",
        "description": "Returns every finalized STT and LLM turn captured during the session, sorted by `index` ascending. The dashboard's session detail page surfaces these under the Transcript card. S2S sessions don't currently produce transcripts (the audio path bypasses our STT/LLM components) — expect an empty `entries` array for those. Interim STT partials are never returned; only finalized turns make it into this response.",
        "operationId": "getSessionTranscript",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Session id."
          }
        ],
        "responses": {
          "200": {
            "description": "Full transcript for the session, ordered by `index`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionTranscript"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Session id is unknown to the calling organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "session not found",
                      "code": "SESSION_NOT_FOUND"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/voice/calls": {
      "post": {
        "summary": "Dial an outbound human call",
        "description": "Places a PSTN call with the supplied org-defined broker's softphone as the other leg. Resolves as soon as the call exists — the PSTN leg is still `initiating`/`ringing`; watch the event stream for `call.answered`. The response carries join credentials for that browser leg: connect immediately, or the first moments after the far end answers are silence.",
        "operationId": "dialHumanCall",
        "tags": [
          "Call control"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HumanCallDialRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Call created. `call` carries both legs (the `browser` leg is the caller's softphone, the `pstn` leg the far end); `join` is the caller's own room credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "call",
                    "join"
                  ],
                  "properties": {
                    "call": {
                      "$ref": "#/components/schemas/HumanCall"
                    },
                    "join": {
                      "$ref": "#/components/schemas/HumanCallJoinCredentials"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      },
      "get": {
        "summary": "List human calls",
        "description": "The organization's calls, newest first. Filters combine via AND and are validated — an unknown `status` is a 400, not an ignored filter.",
        "operationId": "listHumanCalls",
        "tags": [
          "Call control"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "initiating",
                "ringing",
                "active",
                "ended",
                "declined",
                "missed",
                "cancelled",
                "failed"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound",
                "internal"
              ]
            }
          },
          {
            "name": "brokerId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Calls with a `browser` leg owned by this broker."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Calls, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "calls": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HumanCall"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/voice/calls/{callId}": {
      "get": {
        "summary": "Get a human call",
        "operationId": "getHumanCall",
        "tags": [
          "Call control"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CallId"
          }
        ],
        "responses": {
          "200": {
            "description": "The call and every leg on it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HumanCall"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Call or leg not found — including a leg that exists but belongs to another organization or broker.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/voice/calls/{callId}/events": {
      "get": {
        "summary": "List a call's events",
        "description": "The call's ordered history, oldest first — the durable record behind the one-shot webhook stream. Reconcile from here.",
        "operationId": "listHumanCallEvents",
        "tags": [
          "Call control"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CallId"
          }
        ],
        "responses": {
          "200": {
            "description": "Events, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HumanCallEvent"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Call or leg not found — including a leg that exists but belongs to another organization or broker.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/voice/legs/{controlId}/join": {
      "post": {
        "summary": "Mint join credentials for your leg",
        "description": "Room credentials for one of the supplied broker's own `browser` legs. Join first, then `answer`. Tokens are short-lived and minted per join; re-join on reconnect rather than caching one. A leg owned by another broker returns 404, deliberately indistinguishable from a leg that does not exist.",
        "operationId": "joinHumanCallLeg",
        "tags": [
          "Call control"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ControlId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "brokerId"
                ],
                "additionalProperties": false,
                "properties": {
                  "brokerId": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Join credentials for the leg's room.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HumanCallJoinCredentials"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Call or leg not found — including a leg that exists but belongs to another organization or broker.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/voice/legs/{controlId}/actions/{command}": {
      "post": {
        "summary": "Command a live leg",
        "description": "The command surface: `answer`, `hangup`, `bridge`, `hold`, `unhold`, `mute`, `unmute`, `dtmf`, `transfer`, each addressed to the leg it acts on. Commands are idempotent by intent — repeating one is a no-op that still returns the leg. Accepts an API key: server-side automation on live calls is the point of a programmable-voice API. Notable semantics: `hold` is silent (no hold-music source yet); `dtmf` must be addressed to a `pstn` leg and requires a connected browser leg on the call to relay the tones; a warm `transfer` is completed by a later `bridge`.",
        "operationId": "commandHumanCallLeg",
        "tags": [
          "Call control"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ControlId"
          },
          {
            "name": "command",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "answer",
                "hangup",
                "bridge",
                "hold",
                "unhold",
                "mute",
                "unmute",
                "dtmf",
                "transfer"
              ]
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HumanCallCommandRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The leg after the command.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HumanCallCommandResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid payload (`VALIDATION_ERROR`), a command the leg kind cannot take (`UNSUPPORTED_COMMAND`), or a bridge to a leg that is not live or not yours (`BRIDGE_TARGET_INVALID`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "DTMF is only meaningful towards a PSTN leg",
                      "code": "UNSUPPORTED_COMMAND"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Call or leg not found — including a leg that exists but belongs to another organization or broker.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The leg (`LEG_NOT_LIVE`) or call (`CALL_NOT_LIVE`) has already ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "leg is not live",
                      "code": "LEG_NOT_LIVE"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/voice/presence/token": {
      "post": {
        "summary": "Register and mint presence credentials",
        "description": "Marks the supplied broker `available` AND mints credentials for their presence room in one round trip. Connect with `livekit-client` and listen for JSON data messages on topic `speko.human_call` — ring offers and live call events arrive there. A broker with no open presence connection cannot be rung, whatever their status.",
        "operationId": "mintPresenceToken",
        "tags": [
          "Call control"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "brokerId"
                ],
                "additionalProperties": false,
                "properties": {
                  "brokerId": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presence-room credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HumanCallJoinCredentials"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/voice/presence/heartbeat": {
      "post": {
        "summary": "Heartbeat",
        "description": "Keeps the supplied broker reachable. Presence goes stale 90 seconds after the last heartbeat and a stale broker is skipped by inbound routing regardless of stored status — call this on a timer, every 30 seconds is a good default.",
        "operationId": "presenceHeartbeat",
        "tags": [
          "Call control"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "brokerId"
                ],
                "additionalProperties": false,
                "properties": {
                  "brokerId": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The broker's presence after the heartbeat.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrokerPresence"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/voice/presence": {
      "put": {
        "summary": "Set availability",
        "description": "Sets the supplied broker's status. `busy` and `away` keep the heartbeat alive while diverting inbound; `offline` leaves routing entirely.",
        "operationId": "setPresenceStatus",
        "tags": [
          "Call control"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "brokerId",
                  "status"
                ],
                "additionalProperties": false,
                "properties": {
                  "brokerId": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "available",
                      "busy",
                      "away",
                      "offline"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated presence.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrokerPresence"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      },
      "get": {
        "summary": "List the availability roster",
        "description": "Every registered broker in the organization with their status and a computed `reachable`. Build your team's availability board from it.",
        "operationId": "listPresence",
        "tags": [
          "Call control"
        ],
        "responses": {
          "200": {
            "description": "The org roster.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "brokers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BrokerPresence"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/HumanCallingUnavailable"
          }
        }
      }
    },
    "/v1/sms/10dlc": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Get 10DLC registration state",
        "operationId": "getSms10dlcOverview",
        "description": "Needs `speko:read`. Statuses are read back from Telnyx by webhook and by a 15-minute poll.",
        "responses": {
          "200": {
            "description": "Brand, campaign, numbers, fees and readiness",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcOverview"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/brand": {
      "put": {
        "tags": [
          "SMS"
        ],
        "summary": "Save the brand draft",
        "operationId": "saveSms10dlcBrand",
        "description": "No Telnyx call and no fee. A registered brand cannot switch between a business and a sole proprietor (`BRAND_KIND_LOCKED`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Sms10dlcBusinessProfile"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brand",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcBrand"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The workspace uses a brand managed by Speko (`ADMIN_MANAGED_10DLC`), the brand is registered and cannot switch between a business and a sole proprietor (`BRAND_KIND_LOCKED`), or a brand submission is in flight (`SMS_10DLC_OPERATION_IN_PROGRESS`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/brand/submit": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Register or update the brand",
        "operationId": "submitSms10dlcBrand",
        "description": "The first submission of a business brand charges the $4.50 brand registration fee. A sole proprietor brand is charged when its OTP is verified. Updating a registered brand has no fee.",
        "responses": {
          "200": {
            "description": "Brand",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcBrand"
                }
              }
            }
          },
          "402": {
            "description": "The available balance is below the fee plus the $10 minimum (`INSUFFICIENT_CREDITS`). Nothing was charged and no paid Telnyx call was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcInsufficientCredits"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A brand submission is already in progress (`SMS_10DLC_OPERATION_IN_PROGRESS`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Telnyx did not confirm the paid step (`TELNYX_OUTCOME_UNKNOWN`). The fee stays charged until Speko confirms the step with Telnyx or refunds it, within 30 minutes. Do not resubmit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/brand/vetting": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Order enhanced vetting",
        "operationId": "requestSms10dlcVetting",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "vettingClass": {
                    "type": "string",
                    "enum": [
                      "STANDARD",
                      "POLITICAL"
                    ]
                  },
                  "evpId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brand",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcBrand"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Sole proprietor brands are not vetted (`VETTING_NOT_AVAILABLE`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/brand/otp": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Get the sole proprietor OTP status",
        "operationId": "getSms10dlcBrandOtp",
        "responses": {
          "200": {
            "description": "OTP status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcOtpStatus"
                }
              }
            }
          },
          "409": {
            "description": "The brand is not a sole proprietor (`NOT_SOLE_PROPRIETOR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Send the sole proprietor OTP",
        "operationId": "sendSms10dlcBrandOtp",
        "description": "Telnyx texts a PIN to the brand's mobile. It expires after 24 hours.",
        "responses": {
          "202": {
            "description": "Code sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcOtpStatus"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Not a sole proprietor brand, or already verified (`BRAND_ALREADY_VERIFIED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/brand/otp/resend": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Send the sole proprietor OTP again",
        "operationId": "resendSms10dlcBrandOtp",
        "responses": {
          "202": {
            "description": "Code sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcOtpStatus"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/brand/otp/verify": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Verify the sole proprietor OTP",
        "operationId": "verifySms10dlcBrandOtp",
        "description": "Charges the $4.50 brand registration fee when Telnyx accepts the code.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "pin"
                ],
                "properties": {
                  "pin": {
                    "type": "string",
                    "pattern": "^\\d{4,10}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brand",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcBrand"
                }
              }
            }
          },
          "400": {
            "description": "Wrong or expired code (`OTP_INVALID`). The fee was not charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The available balance is below the fee plus the $10 minimum (`INSUFFICIENT_CREDITS`). Nothing was charged and no paid Telnyx call was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcInsufficientCredits"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Another request is checking a code for this brand (`SMS_10DLC_OPERATION_IN_PROGRESS`). Nothing was charged and the code was not sent to Telnyx.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Telnyx did not confirm the paid step (`TELNYX_OUTCOME_UNKNOWN`). The fee stays charged until Speko confirms the step with Telnyx or refunds it, within 30 minutes. Do not resubmit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/campaign": {
      "put": {
        "tags": [
          "SMS"
        ],
        "summary": "Save the campaign draft",
        "operationId": "saveSms10dlcCampaign",
        "description": "A sole proprietor's campaign is saved with use case `SOLE_PROPRIETOR`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Sms10dlcCampaignProfile"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcCampaign"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/campaign/quote": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Price the next campaign submission",
        "operationId": "getSms10dlcCampaignQuote",
        "responses": {
          "200": {
            "description": "Quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcCampaignQuote"
                }
              }
            }
          },
          "400": {
            "description": "No saved campaign (`PROFILE_REQUIRED`), brand not verified (`BRAND_NOT_READY`), or content The Campaign Registry would fail (`CAMPAIGN_CONTENT_INVALID`, with each issue in `problems` and the allowed `subUseCases`): fewer than 2 samples, text below TCR's minimum lengths, or the wrong number of `subUsecases`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A campaign is already under review or approved (`CAMPAIGN_ALREADY_SUBMITTED`), or an appeal's draft changes fields Telnyx cannot update on the submitted campaign (`CAMPAIGN_FIELDS_NOT_UPDATABLE`, with them in `fields`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/campaign/submit": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Submit or resubmit the campaign",
        "operationId": "submitSms10dlcCampaign",
        "description": "Charges the $15 review fee plus three months of the use case's monthly fee before the paid Telnyx call. A rejected campaign is resubmitted on the same Telnyx campaign: its content is updated, and it is appealed only if the update leaves it rejected. An appeal is priced at the use case the campaign is registered under. Carrier rejection refunds the prepaid months the campaign never served.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "appealReason": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "Required to resubmit a campaign rejected as TELNYX_FAILED or MNO_REJECTED."
                  },
                  "quote": {
                    "type": "object",
                    "description": "The `useCase` and `dueNowUsd` you showed from the quote; a changed price returns 409 `CAMPAIGN_QUOTE_CHANGED`.",
                    "required": [
                      "useCase",
                      "dueNowUsd"
                    ],
                    "properties": {
                      "useCase": {
                        "type": "string"
                      },
                      "dueNowUsd": {
                        "type": "number",
                        "minimum": 0
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcCampaign"
                }
              }
            }
          },
          "400": {
            "description": "Brand not verified (`BRAND_NOT_READY`), an appeal without `appealReason` (`APPEAL_REASON_REQUIRED`), or content The Campaign Registry would fail (`CAMPAIGN_CONTENT_INVALID`, with each issue in `problems`). Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The available balance is below the fee plus the $10 minimum (`INSUFFICIENT_CREDITS`). Nothing was charged and no paid Telnyx call was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcInsufficientCredits"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A submission is in flight (`SMS_10DLC_OPERATION_IN_PROGRESS`), a campaign is already under review or approved (`CAMPAIGN_ALREADY_SUBMITTED`), an appeal's draft changes fields Telnyx cannot update on the submitted campaign (`CAMPAIGN_FIELDS_NOT_UPDATABLE`, with them in `fields`), or the price differs from the `quote` sent (`CAMPAIGN_QUOTE_CHANGED`, with the current `useCase` and `dueNowUsd`). Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Telnyx did not confirm the paid step (`TELNYX_OUTCOME_UNKNOWN`). The fee stays charged until Speko confirms the step with Telnyx or refunds it, within 30 minutes. Do not resubmit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/campaign/billing/resume": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Resume billing for an approved campaign",
        "operationId": "resumeSms10dlcCampaignBilling",
        "description": "For a campaign Telnyx approved after its monthly billing ended (it was rejected or lapsed, then provisioned), so it does not send. Charges one month of the campaign's monthly fee up front (`ownCampaign.resumeBillingUsd`), with no review fee and no new campaign, and restarts billing as `current` with the next month charged 30 days later. The same balance rule as every 10DLC fee applies: the fee plus $10 must be available, and credit overrun never applies.",
        "responses": {
          "200": {
            "description": "Campaign, with `billing.status` `current`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcCampaign"
                }
              }
            }
          },
          "402": {
            "description": "The available balance is below the monthly fee plus the $10 minimum (`INSUFFICIENT_CREDITS`). Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcInsufficientCredits"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Billing is not ended or the campaign is not approved (`CAMPAIGN_BILLING_NOT_RESUMABLE`), a campaign submission awaits Telnyx and restarts billing itself (`SMS_10DLC_OPERATION_IN_PROGRESS`), or Speko administrators manage this workspace's 10DLC brand (`ADMIN_MANAGED_10DLC`). Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Speko reads the campaign from Telnyx before charging, and that read failed (`TELNYX_CAMPAIGN_STATUS_FAILED`). Nothing was charged. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/numbers/{phoneNumberId}/assign": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Assign a number to a campaign",
        "operationId": "assignSms10dlcNumber",
        "parameters": [
          {
            "name": "phoneNumberId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "target": {
                    "type": "string",
                    "enum": [
                      "own",
                      "shared"
                    ]
                  },
                  "campaignId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Number",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcPhoneNumber"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A sole proprietor campaign already sends from another number (`SOLE_PROPRIETOR_NUMBER_LIMIT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/10dlc/numbers/{phoneNumberId}/assignment": {
      "delete": {
        "tags": [
          "SMS"
        ],
        "summary": "Remove a number's campaign assignment",
        "operationId": "unassignSms10dlcNumber",
        "parameters": [
          {
            "name": "phoneNumberId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Number",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sms10dlcPhoneNumber"
                }
              }
            }
          },
          "403": {
            "description": "Missing `speko:compliance` (code `INSUFFICIENT_SCOPE`), or the signed-in user is not an owner or admin (`ORG_ADMIN_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/assignees": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List shared-inbox human assignees",
        "operationId": "listSmsAssignees",
        "responses": {
          "200": {
            "description": "Organization members eligible for human assignment",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SmsAssignee"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/messages": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Queue an SMS message",
        "operationId": "sendSmsMessage",
        "description": "Durably validates compliance and reserves credits before queueing. The campaign and brand are derived from the sender number.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsSendRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Message durably queued or scheduled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsMessage"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/SmsSendingNotApproved"
          },
          "409": {
            "description": "Idempotency key reused with a different request"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List SMS messages",
        "operationId": "listSmsMessages",
        "parameters": [
          {
            "name": "conversation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "batch_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from_phone_number_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "recipient",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "description": "Only messages written while this agent handled their conversation.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SmsMessageStatus"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound"
              ]
            }
          },
          {
            "name": "origin",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "api",
                "dashboard",
                "agent_tool",
                "agent_auto_reply",
                "telnyx"
              ]
            }
          },
          {
            "name": "created_after",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "created_before",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "From a previous `next_cursor`. Any other value returns `422 INVALID_CURSOR`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsMessagePage"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/messages/{message_id}": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Get an SMS message",
        "operationId": "getSmsMessage",
        "parameters": [
          {
            "name": "message_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsMessage"
                }
              }
            }
          },
          "404": {
            "description": "Message not found"
          }
        }
      }
    },
    "/v1/sms/messages/{message_id}/cancel": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Cancel a queued or scheduled SMS",
        "operationId": "cancelSmsMessage",
        "parameters": [
          {
            "name": "message_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Canceled message",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsMessage"
                }
              }
            }
          },
          "409": {
            "description": "Message has begun provider submission"
          }
        }
      }
    },
    "/v1/sms/batches": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Queue an SMS batch",
        "operationId": "createSmsBatch",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsBatchRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Batch accepted with per-recipient validation totals",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsBatch"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/SmsSendingNotApproved"
          }
        }
      }
    },
    "/v1/sms/batches/{batch_id}": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Get an SMS batch",
        "operationId": "getSmsBatch",
        "parameters": [
          {
            "name": "batch_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsBatch"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/batches/{batch_id}/messages": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List messages in an SMS batch",
        "operationId": "listSmsBatchMessages",
        "parameters": [
          {
            "name": "batch_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "From a previous `next_cursor`. Any other value returns `422 INVALID_CURSOR`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsMessagePage"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/batches/{batch_id}/cancel": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Cancel all cancelable messages in a batch",
        "operationId": "cancelSmsBatch",
        "parameters": [
          {
            "name": "batch_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Canceled batch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsBatch"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/conversations": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List SMS conversations",
        "operationId": "listSmsConversations",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed",
                "spam"
              ]
            }
          },
          {
            "name": "phone_number_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "assigned_user_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assigned_agent_id",
            "in": "query",
            "description": "Only conversations this agent handles now.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "description": "The agent's inbox: conversations it handles now, plus conversations holding a message written while it handled them.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "last_message_after",
            "in": "query",
            "description": "Only conversations whose last message is at or after this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "recipient",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "unread",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "From a previous `next_cursor`. Any other value returns `422 INVALID_CURSOR`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsConversationPage"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/conversations/{conversation_id}": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Get an SMS conversation",
        "operationId": "getSmsConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsConversation"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "SMS"
        ],
        "summary": "Update assignment, state, timezone, or automation",
        "operationId": "updateSmsConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsConversationUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated conversation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsConversation"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "SMS"
        ],
        "summary": "Immediately redact conversation content",
        "operationId": "redactSmsConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          }
        ],
        "responses": {
          "204": {
            "description": "Content redacted; compliance and billing facts retained"
          }
        }
      }
    },
    "/v1/sms/conversations/{conversation_id}/messages": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List a conversation timeline",
        "operationId": "listSmsConversationMessages",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "From a previous `next_cursor`. Any other value returns `422 INVALID_CURSOR`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chronological message page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsMessagePage"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Reply in a conversation",
        "operationId": "replyToSmsConversation",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsConversationSendRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Reply queued; dashboard replies pause automation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsMessage"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/SmsSendingNotApproved"
          }
        }
      }
    },
    "/v1/sms/conversations/{conversation_id}/read": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Mark a conversation read",
        "operationId": "markSmsConversationRead",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated conversation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsConversation"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/conversations/{conversation_id}/notes": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List internal conversation notes",
        "operationId": "listSmsConversationNotes",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chronological internal notes"
          }
        }
      },
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Add an internal conversation note",
        "operationId": "createSmsConversationNote",
        "parameters": [
          {
            "$ref": "#/components/parameters/SmsConversationId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 10000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Note created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsConversationNote"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/consents": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List SMS consent records",
        "operationId": "listSmsConsents",
        "parameters": [
          {
            "name": "recipient",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "revoked",
                "expired"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Consent records",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SmsConsent"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` for the next page. Null on the last page."
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Record SMS consent",
        "operationId": "createSmsConsent",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsConsentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Consent recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsConsent"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/SmsSendingNotApproved"
          }
        }
      }
    },
    "/v1/sms/consents/import": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Import up to 1,000 consent records",
        "operationId": "importSmsConsents",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "records"
                ],
                "properties": {
                  "records": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 1000,
                    "items": {
                      "$ref": "#/components/schemas/SmsConsentInput"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Idempotent import result"
          },
          "403": {
            "$ref": "#/components/responses/SmsSendingNotApproved"
          }
        }
      }
    },
    "/v1/sms/consents/{consent_id}/revoke": {
      "post": {
        "tags": [
          "SMS"
        ],
        "summary": "Revoke active consent",
        "operationId": "revokeSmsConsent",
        "parameters": [
          {
            "name": "consent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked consent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsConsent"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/suppressions": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "List organization-wide SMS suppressions",
        "operationId": "listSmsSuppressions",
        "parameters": [
          {
            "name": "recipient",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "active",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suppression records",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SmsSuppression"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` for the next page. Null on the last page."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/capabilities": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Get SMS capabilities",
        "operationId": "getSmsCapabilities",
        "description": "Whether the workspace can send SMS. Every workspace receives SMS; sending starts once at least one live number is assigned to an approved 10DLC campaign. The same object is features.smsSending on GET /v1/organization.",
        "responses": {
          "200": {
            "description": "Sending capability",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsCapabilities"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sms/settings": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Get organization SMS settings",
        "operationId": "getSmsSettings",
        "responses": {
          "200": {
            "description": "Settings or null",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/SmsSettings"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "SMS"
        ],
        "summary": "Update organization SMS settings",
        "operationId": "updateSmsSettings",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsSettingsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsSettings"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/SmsSendingNotApproved"
          }
        }
      }
    },
    "/v1/sms/stream": {
      "get": {
        "tags": [
          "SMS"
        ],
        "summary": "Stream live SMS events",
        "operationId": "streamSmsEvents",
        "description": "Server-sent events. Send Last-Event-ID to catch up from PostgreSQL before live Redis events.",
        "parameters": [
          {
            "name": "Last-Event-ID",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "text/event-stream of SMS lifecycle events",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/tts/models": {
      "get": {
        "summary": "Compare TTS models",
        "description": "Every TTS model this organization can route, with all measured benchmarks for one language. Scoped to what you can call: a vendor with no platform key and no BYOK credential is absent unless you pass `include_unroutable=true`.\n\nThere is no composite score. The TTS board publishes no overall rank column, so results come back measured-first and then ordered by the single axis you name. A model with no value on that axis sorts last in either direction, so an unmeasured model cannot win a cheapest-or-fastest sort by having no number.",
        "operationId": "listTtsModels",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "en"
            },
            "description": "Which language cell to report. Robustness is measured for English only."
          },
          {
            "name": "use_case",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "longform",
                "phone"
              ]
            },
            "description": "Narrow to models with at least one voice meeting this use case. Rows included only by `include_unroutable=true` are not narrowed, since no voice evidence exists for a model this organization cannot route."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "natural",
                "latency",
                "cost",
                "robustness"
              ],
              "default": "natural"
            }
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "description": "Defaults to ascending for `latency` and `cost`, descending for the other axes."
          },
          {
            "name": "include_unroutable",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false",
                "1",
                "0"
              ],
              "default": "false"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Model comparison",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string"
                    },
                    "language": {
                      "type": "string"
                    },
                    "sort": {
                      "type": "string"
                    },
                    "order": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TtsModel"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/TtsCatalogMeta"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/tts/voices": {
      "get": {
        "summary": "Browse TTS voices by measured quality",
        "description": "Paginated voice list with measured quality labels and the benchmarks of the model each voice would run on.\n\nOne parameter gets you a long way: `?use_case=longform` or `?use_case=phone`. Longform returns a short list, because it requires the `consistent` label that roughly a fifth of routed voices hold, concentrated in two vendors. When a filter set returns little, `blockers` names the requirements holding the rest back, and each entry says whether relaxing that one alone would admit anything.\n\nEvery row carries `use_with`, the provider, model and voice ids to send to `POST /v1/synthesize`.\n\nAlways scoped to what this organization can route. Unlike `/v1/tts/models` there is no way to widen it.",
        "operationId": "listTtsVoices",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "name": "use_case",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "longform",
                "phone"
              ]
            },
            "description": "Expands to a bundle of quality requirements plus a capacity floor. Combine with `quality` to tighten it."
          },
          {
            "name": "quality",
            "in": "query",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "clean",
                  "full_band",
                  "peak_safe",
                  "longform_stable",
                  "consistent"
                ]
              }
            },
            "description": "Repeatable, and repeats mean AND. `GET /v1/tts/meta` documents what each value asserts."
          },
          {
            "name": "min_chars_per_call",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                5000,
                7500,
                10000,
                20000
              ]
            },
            "description": "A floor. Returns voices that accept at least this much in one call."
          },
          {
            "name": "pace",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "slow",
                "normal",
                "brisk",
                "fast"
              ]
            },
            "description": "An exact bucket. `normal` excludes `brisk`."
          },
          {
            "name": "vendor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Router and platform spellings are accepted: `gemini`, `google-tts` and `google` all select the Gemini voices."
          },
          {
            "name": "gender",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "male",
                "female",
                "neutral"
              ]
            }
          },
          {
            "name": "accent",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter voices by supported language. Exact when a region is named, prefix otherwise."
          },
          {
            "name": "model",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Pin to one model id. Compatible voices are re-paired onto it, so `use_with` names the model you asked for rather than the vendor default, and `voice_compatibility` says whether that pairing is individually verified. The Router's spelling of a model is accepted too: `gemini:gemini-3.8-flash-tts` is `google-tts:gemini-3.8-flash-tts`."
          },
          {
            "name": "benchmark_language",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "en"
            },
            "description": "Which language the embedded model benchmarks report."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "model_naturalness",
                "latency",
                "model_cost",
                "pace",
                "max_chars",
                "name"
              ],
              "default": "model_naturalness"
            },
            "description": "Measured voices sort before unmeasured ones on every axis except `name`, which is purely alphabetical. A voice with no value on the chosen axis sorts last in either direction."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "From a previous `next_cursor`. Tied to the filter and sort it was issued for; changing either returns `INVALID_CURSOR` instead of silently reshuffling the page."
          },
          {
            "name": "include_unmeasured",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false",
                "1",
                "0"
              ],
              "default": "true"
            },
            "description": "Set `false` to drop voices that were never measured. Voices that were measured and cleared no label (`verdict: measured_no_labels`) are kept, since that is a result rather than a gap."
          }
        ],
        "responses": {
          "200": {
            "description": "Voice list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string"
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total matching the filters, not the page size."
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TtsVoice"
                      }
                    },
                    "models": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/TtsModel"
                      },
                      "description": "The models this page references, keyed by id, included once rather than repeated per voice."
                    },
                    "facets": {
                      "type": "object",
                      "description": "Self-excluding counts: each group is computed with its own filter dropped, so `vendor` still lists every vendor when `?vendor=` is set. `quality` is multi-valued, so its whole group is dropped and each axis reports what that axis alone would return with the other slots still applied. The quality counts therefore do not move when the quality selection changes, and a count is not \"what relaxing this one requirement would give\" once two or more axes are selected. For that, read `blockers`.",
                      "properties": {
                        "vendor": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "accent": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "quality": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    },
                    "blockers": {
                      "type": "array",
                      "description": "Which requirements are holding voices back, so a short result set explains itself. `param` and `value` are what to change or drop to widen the search. `sole_binding` says whether relaxing that one requirement alone would admit anything; when it is false, two or more bind together and `blocks` is that requirement's cost on its own. Counts cover measured voices only, since an unmeasured voice holds no label and so fails every requirement rather than any one of them.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "param": {
                            "type": "string",
                            "enum": [
                              "quality",
                              "pace",
                              "min_chars_per_call"
                            ],
                            "description": "The query parameter this requirement came from."
                          },
                          "value": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "integer"
                              }
                            ],
                            "description": "The value to drop or relax on that parameter."
                          },
                          "label": {
                            "type": "string"
                          },
                          "blocks": {
                            "type": "integer"
                          },
                          "hint": {
                            "type": "string"
                          },
                          "sole_binding": {
                            "type": "boolean",
                            "description": "True when relaxing this requirement alone would admit more voices. False when no single relaxation helps."
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/TtsCatalogMeta"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (`VALIDATION_ERROR`); `INVALID_CURSOR` when a cursor is reused across a different filter or sort; `CURSOR_EXPIRED` when the row a cursor resumes after has left the result set. In both cursor cases, restart the listing without a cursor."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/tts/voices/{vendor}/{voiceId}": {
      "get": {
        "summary": "One voice and the model it runs on",
        "description": "Everything the list returns for a single voice, plus the model it would run on and that model’s benchmarks. Quality labels state what was checked, and `quality.verdict` says whether the voice was measured at all.",
        "operationId": "getTtsVoice",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "name": "vendor",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "voiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Case-sensitive, exactly as the provider spells it."
          }
        ],
        "responses": {
          "200": {
            "description": "Voice detail",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/TtsVoice"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "model": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/TtsModel"
                            }
                          ],
                          "nullable": true
                        },
                        "meta": {
                          "$ref": "#/components/schemas/TtsCatalogMeta"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such voice, or it is not routable for this organization."
          }
        }
      }
    },
    "/v1/tts/meta": {
      "get": {
        "summary": "The quality vocabulary",
        "description": "What every quality label asserts, plus the pace buckets, capacity rungs and use-case bundles. Every value the filters accept is listed here.",
        "operationId": "getTtsMeta",
        "tags": [
          "Voice"
        ],
        "responses": {
          "200": {
            "description": "Vocabulary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "quality": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "means": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "pace": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "range_wpm": {
                            "type": "array",
                            "items": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    },
                    "chars_per_call": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "integer"
                          },
                          "label": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "use_cases": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "hint": {
                            "type": "string"
                          },
                          "requires": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "min_chars_per_call": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/agents/{id}/versions": {
      "get": {
        "summary": "List agent versions",
        "description": "Every version of the agent, newest first (descending `version_number`). The list is not paginated and includes the pending draft, if any, as a row with `status: \"draft\"`.",
        "operationId": "listAgentVersions",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Versions, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AgentVersion"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/AgentNotFound"
          }
        }
      }
    },
    "/v1/agents/{id}/deploy": {
      "post": {
        "summary": "Deploy an agent",
        "description": "Makes a version live; the previous live version is archived. With no `session_config`, the pending draft is published (same version number, `published_draft: true`), or, when there is no draft, the agent as it currently stands is snapshotted as a new version. A `session_config` publishes that config as a new version and leaves any draft untouched. Refused with 409 `GATE_BLOCKED` when the organization has reliability-gate blocking enabled and the current live version failed the gate, unless `override` is true.",
        "operationId": "deployAgent",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Optional. A missing or unparseable body is treated as `{}`.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentVersionDeployRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The version that is now live",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentVersionDeployResponse"
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation, or the deploy would move the agent onto a voice none of its allowed TTS providers can speak.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation": {
                    "value": {
                      "error": "Invalid request",
                      "code": "VALIDATION_ERROR"
                    }
                  },
                  "voiceProvider": {
                    "value": {
                      "error": "Voice 'Kore' is a google voice, but google is not in stackPreferences.allowedProviders.tts. Add a google model (e.g. google:<model>) to stackPreferences.allowedProviders.tts, or pick a elevenlabs voice.",
                      "code": "VOICE_PROVIDER_NOT_ALLOWED"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No agent with this id exists in the authenticated organization, or the draft was published or discarded concurrently.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The deploy was refused. Nothing was published.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "gateBlocked": {
                    "value": {
                      "error": "This agent's current live version failed the pre-prod reliability gate. Fix the flagged issues, or redeploy with override=true to ship anyway.",
                      "code": "GATE_BLOCKED"
                    }
                  },
                  "incompatibleRuntimeMode": {
                    "value": {
                      "error": "Pipecat managed agents support cascade mode only.",
                      "code": "INCOMPATIBLE_RUNTIME_MODE"
                    }
                  },
                  "pipecatWorkflowRequired": {
                    "value": {
                      "error": "A Pipecat workflow is required before this agent can be published.",
                      "code": "PIPECAT_WORKFLOW_REQUIRED"
                    }
                  },
                  "mcpAuthRequired": {
                    "value": {
                      "error": "MCP tool \"lookup_booking\" needs its server signed in again. Reconnect it before publishing.",
                      "code": "MCP_AUTH_REQUIRED"
                    }
                  },
                  "mcpSchemaDrift": {
                    "value": {
                      "error": "MCP tool \"lookup_booking\" changed or is unavailable. Refresh it before publishing.",
                      "code": "MCP_SCHEMA_DRIFT"
                    }
                  },
                  "workflowConflict": {
                    "value": {
                      "error": "The workflow changed while it was being published. Review and publish again.",
                      "code": "WORKFLOW_CONFLICT"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The deploy would move the agent onto a hosted voice that the key its calls use does not have.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "Voice 'aBcDeFgHiJkLmNoPqRsT' is not available to hosted agents: it is not in Speko's ElevenLabs account. Use a voice from the voice picker or a Speko voice clone.",
                      "code": "VOICE_NOT_AVAILABLE"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}/rollback": {
      "post": {
        "summary": "Roll back an agent",
        "description": "Restores a previous version's config and workflow graph as a NEW live version (next version number, `source: \"rollback:<n>\"`) and archives the current live one. Refused with 409 `GATE_BLOCKED` on the same terms as deploy, unless `override` is true.",
        "operationId": "rollbackAgent",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentVersionRollbackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new live version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentVersion"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The agent, or the target version, does not exist in the authenticated organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "agentNotFound": {
                    "value": {
                      "error": "not found",
                      "code": "NOT_FOUND"
                    }
                  },
                  "versionNotFound": {
                    "value": {
                      "error": "target version not found",
                      "code": "VERSION_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The current live version is gate-blocked and `override` was not set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "This agent's current live version failed the pre-prod reliability gate. Fix the flagged issues, or redeploy with override=true to ship anyway.",
                      "code": "GATE_BLOCKED"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}/draft": {
      "get": {
        "summary": "Get the agent draft",
        "description": "The agent's unpublished changes and which config fields they would change on deploy. An agent with no draft returns 200 with `has_draft: false`.",
        "operationId": "getAgentDraft",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDraft"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No agent with this id exists in the authenticated organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "agent not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Save the agent draft",
        "description": "Lays a partial agent update over the current draft (or over the live agent when there is no draft) and stores it as the draft. Calls keep serving the live version until `POST /v1/agents/{id}/deploy`. A save that brings the config back to match live, on a draft with no workflow changes of its own, discards the draft and returns `has_draft: false`.",
        "operationId": "saveAgentDraft",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The `PATCH /v1/agents/{id}` body. `webhooks` and `outboundDtmf` are rejected with 400; update them with `PATCH`. Only fields the draft snapshot carries are stored: `name`, `systemPrompt`, `voice`, `voiceInstructions`, `firstMessage`, `inboundFirstMessage`, `outboundFirstMessage`, `timezone`, `intent`, `llmOptions`, `stackPreferences`, `sttOptions`, `ttsOptions`, `runMode`, `runtime`, `backgroundAudio`, `audioOutput`, `speechNormalization`, `idleRePrompts`, `sttMissRecovery`, `turnHandling`, `languageRouting`. Other fields pass validation but are not saved.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAgentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft state after the save",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDraft"
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON, failed validation, contains a field that cannot be drafted, or produced an invalid Pipecat workflow.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidJson": {
                    "value": {
                      "error": "Request body is not valid JSON.",
                      "code": "INVALID_JSON"
                    }
                  },
                  "validation": {
                    "value": {
                      "error": "Invalid request",
                      "code": "VALIDATION_ERROR"
                    }
                  },
                  "webhooksNotDraftable": {
                    "value": {
                      "error": "webhooks cannot be drafted — update them directly on the agent",
                      "code": "WEBHOOKS_NOT_DRAFTABLE"
                    }
                  },
                  "outboundDtmfNotDraftable": {
                    "value": {
                      "error": "outboundDtmf cannot be drafted — update it directly on the agent",
                      "code": "OUTBOUND_DTMF_NOT_DRAFTABLE"
                    }
                  },
                  "invalidWorkflow": {
                    "value": {
                      "error": "tools: duplicate tool name 'lookup_booking'",
                      "code": "INVALID_WORKFLOW"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No agent with this id exists in the authenticated organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "agent not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Discard the agent draft",
        "description": "Deletes the pending draft, config and workflow edits alike. Idempotent: an agent with no draft also returns 200, and the response always has `has_draft: false`.",
        "operationId": "discardAgentDraft",
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft state after the discard",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDraft"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No agent with this id exists in the authenticated organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "agent not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{id}/end": {
      "post": {
        "summary": "End a session",
        "description": "Ends a live session in your organization. A room-hosted session (WebRTC or phone leg) has its room deleted and returns 202 `ending`; the room webhooks then run the close and record `end_reason: \"ended_by_api\"`. A provider-direct speech-to-speech session or a Pipecat session is closed in the request and returns 200 `ended`. Idempotent: ending an already-ended session returns 200 `already_ended` with its `ended_at`.",
        "operationId": "endSession",
        "tags": [
          "Voice"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Session closed by this request (`ended`) or already closed before it (`already_ended`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionEndResult"
                },
                "examples": {
                  "ended": {
                    "value": {
                      "ok": true,
                      "status": "ended",
                      "ended_at": "2026-10-27T12:00:00.000Z"
                    }
                  },
                  "already_ended": {
                    "value": {
                      "ok": true,
                      "status": "already_ended",
                      "ended_at": "2026-10-27T11:58:41.000Z"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Room deleted (or already gone). The session closes asynchronously when the room webhook arrives.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionEndResult"
                },
                "examples": {
                  "ending": {
                    "value": {
                      "ok": true,
                      "status": "ending"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The session is room-hosted but has no room to tear down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "call has no room to tear down",
                      "code": "NO_ROOM"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No session with this id in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "not found",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "The end request could not be recorded, so the room was left running (or, after a failed room delete, the failure could not be recorded).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "failed to record the session end request",
                      "code": "END_EVENT_FAILED"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Deleting the room failed. The session may still be live; retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": "failed to end the session",
                      "code": "END_FAILED"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "The room service this session needs is not configured on this server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rooms": {
                    "value": {
                      "error": "room service is not configured on this server",
                      "code": "ROOMS_DISABLED"
                    }
                  },
                  "daily": {
                    "value": {
                      "error": "Daily room service is not configured on this server",
                      "code": "DAILY_DISABLED"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/voices/search": {
      "post": {
        "summary": "Search the voice index",
        "description": "Searches the platform-wide voice index by free-text description, by similarity to a reference voice, or by exact filters. Results are not filtered by which providers your organization can route; check `routerPinnable` and the `/v1/tts/voices` catalog before using a voice. When `similarTo` is set, `description` is ignored and the reference voice itself is excluded.",
        "operationId": "searchVoices",
        "tags": [
          "Voice library"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoiceSearchRequest"
              },
              "examples": {
                "description": {
                  "value": {
                    "description": "warm conversational support agent",
                    "filters": {
                      "language": "en",
                      "gender": "female"
                    },
                    "topK": 5
                  }
                },
                "similar": {
                  "value": {
                    "similarTo": {
                      "vendor": "cartesia",
                      "voiceId": "f9836c6e-a0bd-460e-9d3c-f7299fa60f94"
                    },
                    "topK": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked voices plus facet counts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceSearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON, fails validation, or names none of `description`, `similarTo` or `filters`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation": {
                    "value": {
                      "error": "Invalid request",
                      "code": "VALIDATION_ERROR",
                      "issues": [
                        {
                          "path": "",
                          "message": "At least one search criterion is required"
                        }
                      ]
                    }
                  },
                  "invalid_json": {
                    "value": {
                      "error": "Request body is not valid JSON.",
                      "code": "INVALID_JSON"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The `similarTo` reference voice is not in the index. Note the body shape: `error` is an object, not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceSearchNotFoundError"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": {
                        "code": "VOICE_NOT_FOUND"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/voices/{vendor}/{voiceId}": {
      "get": {
        "summary": "One voice from the voice index",
        "description": "Returns the full voice-index record for one voice: the raw index row in camelCase, with provenance, timestamps and the model lists it was indexed against. Unlike `GET /v1/tts/voices/{vendor}/{voiceId}`, it is not scoped to the providers your organization can route, keeps `qa:` quality tokens inside `styles`, and returns no `use_with` triple, model detail or benchmarks.",
        "operationId": "getVoice",
        "tags": [
          "Voice library"
        ],
        "parameters": [
          {
            "name": "vendor",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "voiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Case-sensitive, exactly as the provider spells it."
          }
        ],
        "responses": {
          "200": {
            "description": "Voice record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceSearchProfile"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No voice with this vendor and id in the index. Note the body shape: `error` is an object, not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceSearchNotFoundError"
                },
                "examples": {
                  "default": {
                    "value": {
                      "error": {
                        "code": "VOICE_NOT_FOUND"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/errors": {
      "get": {
        "summary": "List error codes",
        "description": "The full catalog of error codes the API can return, with category, `retryable`, and a written meaning and hint where one exists. Public: no API key needed.",
        "operationId": "listErrors",
        "tags": [
          "Errors"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Every registered error code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorCatalog"
                }
              }
            }
          }
        }
      }
    },
    "/v1/errors/{code}": {
      "get": {
        "summary": "Explain an error code",
        "description": "One error code from the catalog. Case-insensitive, so a code pasted from a log works as is. Public: no API key needed.",
        "operationId": "getError",
        "tags": [
          "Errors"
        ],
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Error code, for example `PIPELINE_CONFIG_TOO_LARGE`."
          }
        ],
        "responses": {
          "200": {
            "description": "The code's explanation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorCodeDetail"
                }
              }
            }
          },
          "404": {
            "description": "The code is not in the catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "NOT_FOUND": {
                    "value": {
                      "error": "No such error code.",
                      "code": "NOT_FOUND"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key (sk_live_...)"
      }
    },
    "parameters": {
      "Id": {
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Resource id."
      },
      "CallId": {
        "name": "callId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Call id."
      },
      "ControlId": {
        "name": "controlId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Leg control id — the opaque command handle returned on every leg (the Telnyx `call_control_id` equivalent)."
      },
      "SmsConversationId": {
        "name": "conversation_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "schemas": {
      "Sms10dlcBusinessProfile": {
        "oneOf": [
          {
            "type": "object",
            "title": "Business",
            "required": [
              "entityType",
              "legalName",
              "displayName",
              "vertical",
              "website",
              "street",
              "city",
              "state",
              "postalCode",
              "supportEmail",
              "supportPhone",
              "authorizedContact"
            ],
            "properties": {
              "entityType": {
                "type": "string",
                "enum": [
                  "PRIVATE_PROFIT",
                  "PUBLIC_PROFIT",
                  "NON_PROFIT",
                  "GOVERNMENT"
                ]
              },
              "legalName": {
                "type": "string"
              },
              "displayName": {
                "type": "string"
              },
              "vertical": {
                "type": "string"
              },
              "ein": {
                "type": "string"
              },
              "website": {
                "type": "string",
                "format": "uri"
              },
              "country": {
                "type": "string",
                "enum": [
                  "US",
                  "CA"
                ],
                "default": "US"
              },
              "street": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "supportEmail": {
                "type": "string",
                "format": "email"
              },
              "supportPhone": {
                "type": "string"
              },
              "authorizedContact": {
                "type": "object",
                "required": [
                  "firstName",
                  "lastName",
                  "email",
                  "phone"
                ],
                "properties": {
                  "firstName": {
                    "type": "string"
                  },
                  "lastName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string",
                    "description": "E.164. For a sole proprietor, the US or Canadian mobile Telnyx texts the OTP to."
                  }
                }
              }
            }
          },
          {
            "type": "object",
            "title": "Sole proprietor",
            "required": [
              "entityType",
              "displayName",
              "vertical",
              "street",
              "city",
              "state",
              "postalCode",
              "supportEmail",
              "authorizedContact"
            ],
            "properties": {
              "entityType": {
                "type": "string",
                "enum": [
                  "SOLE_PROPRIETOR"
                ]
              },
              "displayName": {
                "type": "string"
              },
              "vertical": {
                "type": "string"
              },
              "website": {
                "type": "string",
                "format": "uri"
              },
              "country": {
                "type": "string",
                "enum": [
                  "US",
                  "CA"
                ],
                "default": "US"
              },
              "street": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "supportEmail": {
                "type": "string",
                "format": "email"
              },
              "supportPhone": {
                "type": "string"
              },
              "authorizedContact": {
                "type": "object",
                "required": [
                  "firstName",
                  "lastName",
                  "email",
                  "phone"
                ],
                "properties": {
                  "firstName": {
                    "type": "string"
                  },
                  "lastName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string",
                    "description": "E.164. For a sole proprietor, the US or Canadian mobile Telnyx texts the OTP to."
                  }
                }
              }
            }
          }
        ],
        "discriminator": {
          "propertyName": "entityType"
        }
      },
      "Sms10dlcCampaignProfile": {
        "type": "object",
        "required": [
          "description",
          "messageFlow",
          "optInKeywords",
          "optInMessage",
          "helpKeywords",
          "helpMessage",
          "optOutKeywords",
          "optOutMessage",
          "sampleMessages"
        ],
        "properties": {
          "useCase": {
            "type": "string",
            "default": "AGENTS_FRANCHISES"
          },
          "description": {
            "type": "string"
          },
          "messageFlow": {
            "type": "string"
          },
          "optInKeywords": {
            "type": "string"
          },
          "optInMessage": {
            "type": "string"
          },
          "helpKeywords": {
            "type": "string"
          },
          "helpMessage": {
            "type": "string"
          },
          "optOutKeywords": {
            "type": "string"
          },
          "optOutMessage": {
            "type": "string"
          },
          "sampleMessages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 5,
            "items": {
              "type": "string"
            },
            "description": "Submitting needs at least 2, each at least 20 characters."
          },
          "subUsecases": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "type": "string",
              "enum": [
                "2FA",
                "ACCOUNT_NOTIFICATION",
                "CUSTOMER_CARE",
                "DELIVERY_NOTIFICATION",
                "FRAUD_ALERT",
                "HIGHER_EDUCATION",
                "MARKETING",
                "POLLING_VOTING",
                "PUBLIC_SERVICE_ANNOUNCEMENT",
                "SECURITY_ALERT"
              ]
            },
            "description": "TCR sub-use cases. MIXED and LOW_VOLUME campaigns need 2 to 5 and SOLE_PROPRIETOR at least 1; the quote returns the exact count Telnyx allows for the use case. Ignored for other use cases."
          },
          "embeddedLink": {
            "type": "boolean"
          },
          "embeddedPhone": {
            "type": "boolean"
          },
          "embeddedLinkSample": {
            "type": "string"
          },
          "privacyPolicyLink": {
            "type": "string"
          },
          "termsAndConditionsLink": {
            "type": "string"
          }
        }
      },
      "Sms10dlcBrand": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "none",
              "draft",
              "pending",
              "verified",
              "failed"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "standard",
              "sole_proprietor"
            ]
          },
          "otpRequired": {
            "type": "boolean",
            "description": "A sole proprietor brand waiting for its OTP."
          },
          "rawStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "identityStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "telnyxBrandId": {
            "type": [
              "string",
              "null"
            ]
          },
          "tcrBrandId": {
            "type": [
              "string",
              "null"
            ]
          },
          "profile": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Sms10dlcBusinessProfile"
              },
              {
                "type": "null"
              }
            ]
          },
          "reviewErrors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "submittedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Sms10dlcCampaignBilling": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "current",
              "grace",
              "suspended",
              "ended"
            ],
            "description": "`grace`: a month is unpaid and SMS keeps sending until `graceEndsAt`. `suspended`: sending is paused until the month is paid."
          },
          "monthlyFeeUsd": {
            "type": "number"
          },
          "nextChargeAt": {
            "type": "string",
            "format": "date-time"
          },
          "graceEndsAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "suspendedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "endedReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "rejected",
              "deactivated",
              null
            ]
          }
        }
      },
      "Sms10dlcCampaign": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "none",
              "draft",
              "pending",
              "active",
              "failed"
            ]
          },
          "rawStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "telnyxCampaignId": {
            "type": [
              "string",
              "null"
            ]
          },
          "tcrCampaignId": {
            "type": [
              "string",
              "null"
            ]
          },
          "useCase": {
            "type": "string"
          },
          "profile": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Sms10dlcCampaignProfile"
              },
              {
                "type": "null"
              }
            ]
          },
          "reviewErrors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "submittedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "resubmit": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "appeal",
              "recreate",
              null
            ]
          },
          "lockedFields": {
            "type": [
              "object",
              "null"
            ],
            "description": "For an `appeal`, the fields Telnyx cannot change on the submitted campaign, keyed by field name, with their registered values (`null` for a field left empty). A submission whose draft changes one is refused with 409 `CAMPAIGN_FIELDS_NOT_UPDATABLE`. Null when every field can be submitted.",
            "additionalProperties": {
              "type": [
                "string",
                "boolean",
                "null"
              ]
            }
          },
          "resumeBillingUsd": {
            "type": [
              "number",
              "null"
            ],
            "description": "What `POST /v1/sms/10dlc/campaign/billing/resume` charges now (one month of the monthly fee) for a campaign Telnyx approved after its billing ended. Null when billing cannot be resumed."
          },
          "billing": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Sms10dlcCampaignBilling"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Sms10dlcFees": {
        "type": "object",
        "properties": {
          "availableBalanceUsd": {
            "type": "number"
          },
          "brandRegistrationUsd": {
            "type": "number"
          },
          "brandRegistrationCharged": {
            "type": "boolean"
          },
          "campaignReviewUsd": {
            "type": "number"
          },
          "campaignPrepaidMonths": {
            "type": "integer"
          },
          "minimumBalanceHeadroomUsd": {
            "type": "number"
          }
        }
      },
      "Sms10dlcCampaignQuote": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "create",
              "appeal",
              "recreate"
            ]
          },
          "useCase": {
            "type": "string"
          },
          "reviewFeeUsd": {
            "type": "number"
          },
          "monthlyFeeUsd": {
            "type": "number"
          },
          "prepaidMonths": {
            "type": "integer"
          },
          "dueNowUsd": {
            "type": "number"
          },
          "minimumBalanceUsd": {
            "type": "number"
          },
          "availableBalanceUsd": {
            "type": "number"
          },
          "sufficientBalance": {
            "type": "boolean"
          },
          "subUseCases": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              }
            },
            "description": "How many `subUsecases` the use case takes, from Telnyx. `max` 0 means it takes none."
          }
        }
      },
      "Sms10dlcOtpStatus": {
        "type": "object",
        "properties": {
          "referenceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "deliveryStatus": {
            "type": "string",
            "description": "NOT_SENT, PENDING, DELIVERED_HANDSET, DELIVERY_FAILED, VERIFIED or EXPIRED."
          },
          "requestedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "verifiedAt": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Sms10dlcPhoneNumber": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "e164": {
            "type": "string"
          },
          "smsCampaignId": {
            "type": [
              "string",
              "null"
            ]
          },
          "smsAssignmentStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "assignedTo": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "own",
              "admin_assigned",
              "shared",
              null
            ]
          },
          "smsEligible": {
            "type": "boolean"
          }
        }
      },
      "Sms10dlcOverview": {
        "type": "object",
        "properties": {
          "managementMode": {
            "type": "string",
            "enum": [
              "self_service",
              "admin_assigned"
            ]
          },
          "ownBrand": {
            "$ref": "#/components/schemas/Sms10dlcBrand"
          },
          "ownCampaign": {
            "$ref": "#/components/schemas/Sms10dlcCampaign"
          },
          "numbers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sms10dlcPhoneNumber"
            }
          },
          "smsReady": {
            "type": "boolean"
          },
          "activeCampaignSource": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "own",
              "admin_assigned",
              "shared",
              null
            ]
          },
          "fees": {
            "$ref": "#/components/schemas/Sms10dlcFees"
          }
        }
      },
      "Sms10dlcInsufficientCredits": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "feeUsd": {
                "type": "number"
              },
              "requiredUsd": {
                "type": "number"
              },
              "availableUsd": {
                "type": "number"
              }
            }
          }
        ]
      },
      "SmsAssignee": {
        "type": "object",
        "required": [
          "id",
          "name",
          "email",
          "role"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "role": {
            "type": "string"
          }
        }
      },
      "SmsConversationNote": {
        "type": "object",
        "required": [
          "id",
          "conversation_id",
          "created_by_user_id",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "conversation_id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_by_user_id": {
            "type": "string"
          },
          "redacted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SmsMessageStatus": {
        "type": "string",
        "enum": [
          "queued",
          "scheduled",
          "submitting",
          "accepted",
          "sent",
          "delivered",
          "delivery_failed",
          "delivery_unconfirmed",
          "rejected",
          "submission_unknown",
          "canceled",
          "received"
        ]
      },
      "SmsMessage": {
        "type": "object",
        "required": [
          "id",
          "conversation_id",
          "phone_number_id",
          "from_phone_number_id",
          "direction",
          "origin",
          "from",
          "to",
          "status",
          "estimated_segments",
          "segment_count",
          "charged_micro_usd",
          "metadata",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "conversation_id": {
            "type": "string",
            "format": "uuid"
          },
          "batch_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "phone_number_id": {
            "type": "string",
            "format": "uuid",
            "description": "The workspace number of the conversation, for inbound and outbound messages alike."
          },
          "from_phone_number_id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The agent handling the conversation when the message was written. It never changes afterward. `null` when no agent handled it."
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "origin": {
            "type": "string",
            "enum": [
              "api",
              "dashboard",
              "agent_tool",
              "agent_auto_reply",
              "telnyx"
            ]
          },
          "from": {
            "type": "string",
            "pattern": "^\\+[1-9]\\d{6,14}$"
          },
          "to": {
            "type": "string",
            "pattern": "^\\+[1-9]\\d{6,14}$"
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1600
          },
          "campaign_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "brand_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "campaign_snapshot": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "consent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "consent_basis": {
            "type": [
              "string",
              "null"
            ]
          },
          "recipient_timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "requested_send_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "effective_send_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "terminal_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/SmsMessageStatus"
          },
          "provider_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "encoding": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "gsm7",
              "ucs2",
              null
            ]
          },
          "estimated_segments": {
            "type": "integer",
            "minimum": 1
          },
          "segment_count": {
            "type": "integer",
            "minimum": 1
          },
          "estimated": {
            "type": "object",
            "required": [
              "encoding",
              "units",
              "segments",
              "per_segment"
            ],
            "properties": {
              "encoding": {
                "type": "string",
                "enum": [
                  "gsm7",
                  "ucs2"
                ]
              },
              "units": {
                "type": "integer"
              },
              "segments": {
                "type": "integer"
              },
              "per_segment": {
                "type": "integer"
              }
            }
          },
          "charged_micro_usd": {
            "type": "string"
          },
          "provider_cost_micro_usd": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "detail": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SmsMessagePage": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SmsMessage"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SmsSendRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "from_phone_number_id",
          "to",
          "text"
        ],
        "properties": {
          "from_phone_number_id": {
            "type": "string",
            "format": "uuid"
          },
          "to": {
            "type": "string",
            "pattern": "^\\+[1-9]\\d{6,14}$"
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1600
          },
          "send_at": {
            "type": "string",
            "format": "date-time",
            "description": "At most 90 days ahead."
          },
          "consent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "recipient_timezone": {
            "type": [
              "string",
              "null"
            ],
            "description": "IANA timezone required for proactive traffic unless stored consent supplies it."
          },
          "metadata": {
            "type": "object",
            "maxProperties": 50,
            "additionalProperties": true
          },
          "expected_agent_id": {
            "type": "string",
            "description": "Send from one agent's inbox to make the write conditional: it is refused with 409 `SMS_THREAD_AGENT_CHANGED` unless this agent still handles the conversation. Omit it to write from the workspace inbox."
          }
        }
      },
      "SmsConversationSendRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1600
          },
          "send_at": {
            "type": "string",
            "format": "date-time"
          },
          "consent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "recipient_timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object",
            "maxProperties": 50,
            "additionalProperties": true
          },
          "expected_agent_id": {
            "type": "string",
            "description": "Send from one agent's inbox to make the write conditional: it is refused with 409 `SMS_THREAD_AGENT_CHANGED` unless this agent still handles the conversation. Omit it to write from the workspace inbox."
          }
        }
      },
      "SmsBatchRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "from_phone_number_id",
          "recipients"
        ],
        "properties": {
          "from_phone_number_id": {
            "type": "string",
            "format": "uuid"
          },
          "send_at": {
            "type": "string",
            "format": "date-time"
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "items": {
              "type": "object",
              "required": [
                "to",
                "text"
              ],
              "properties": {
                "to": {
                  "type": "string"
                },
                "text": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 1600
                },
                "consent_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "recipient_timezone": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "metadata": {
                  "type": "object",
                  "maxProperties": 50,
                  "additionalProperties": true
                }
              }
            }
          }
        }
      },
      "SmsBatch": {
        "type": "object",
        "required": [
          "id",
          "from_phone_number_id",
          "status",
          "total_count",
          "accepted_count",
          "rejected_count",
          "delivered_count",
          "failed_count",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "from_phone_number_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "scheduled",
              "processing",
              "completed",
              "partially_failed",
              "failed",
              "canceled"
            ]
          },
          "requested_send_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "total_count": {
            "type": "integer"
          },
          "accepted_count": {
            "type": "integer"
          },
          "rejected_count": {
            "type": "integer"
          },
          "delivered_count": {
            "type": "integer"
          },
          "failed_count": {
            "type": "integer"
          },
          "canceled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SmsConversation": {
        "type": "object",
        "required": [
          "id",
          "phone_number_id",
          "remote_phone_number",
          "status",
          "automation_status",
          "unread_count",
          "last_message_at",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "phone_number_id": {
            "type": "string",
            "format": "uuid"
          },
          "remote_phone_number": {
            "type": "string"
          },
          "campaign_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "campaign_snapshot": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "closed",
              "spam"
            ]
          },
          "automation_status": {
            "type": "string",
            "enum": [
              "disabled",
              "enabled",
              "paused"
            ]
          },
          "assigned_user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "assigned_agent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The agent handling the conversation: `agent_override_id` when set, otherwise the current agent of the conversation's phone number."
          },
          "agent_override_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "An agent pinned to this conversation. `null` means the conversation follows its phone number's agent."
          },
          "unread_count": {
            "type": "integer"
          },
          "recipient_timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_inbound_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_outbound_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_message_at": {
            "type": "string",
            "format": "date-time"
          },
          "content_redacted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SmsConversationPage": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SmsConversation"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SmsConversationUpdate": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "open",
              "closed",
              "spam"
            ]
          },
          "assigned_user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "assigned_agent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pins an agent to the conversation. `null` removes the pin, so the conversation follows its phone number's agent again."
          },
          "automation_status": {
            "type": "string",
            "enum": [
              "disabled",
              "enabled",
              "paused"
            ]
          },
          "recipient_timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "expected_agent_id": {
            "type": "string",
            "description": "Send from one agent's inbox to make the write conditional: it is refused with 409 `SMS_THREAD_AGENT_CHANGED` unless this agent still handles the conversation. Omit it to write from the workspace inbox."
          }
        }
      },
      "SmsConsentInput": {
        "type": "object",
        "required": [
          "recipient",
          "campaign_id",
          "source"
        ],
        "properties": {
          "recipient": {
            "type": "string",
            "pattern": "^\\+[1-9]\\d{6,14}$"
          },
          "campaign_id": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "inbound",
              "api",
              "keyword",
              "webform",
              "paper",
              "verbal",
              "import"
            ]
          },
          "proof_reference": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048
          },
          "proof": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64000
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "captured_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "SmsConsent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SmsConsentInput"
          },
          {
            "type": "object",
            "required": [
              "id",
              "status",
              "captured_at",
              "created_at"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": "string",
                "enum": [
                  "active",
                  "revoked",
                  "expired"
                ]
              },
              "proof_hash": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "revoked_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "revoked_reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "SmsSendingCapability": {
        "type": "object",
        "required": [
          "enabled",
          "reason"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "True once at least one live number is assigned to an approved 10DLC campaign: the workspace's own business or sole proprietor campaign, a campaign Speko assigned to it, or Speko's shared campaign where enabled."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "no_approved_campaign",
              "campaign_billing_suspended",
              null
            ],
            "description": "Null when sending is enabled. no_approved_campaign: no number is on an approved campaign yet. campaign_billing_suspended: the workspace's own campaign fee is unpaid past its grace period."
          }
        }
      },
      "SmsCapabilities": {
        "type": "object",
        "required": [
          "sending"
        ],
        "properties": {
          "sending": {
            "$ref": "#/components/schemas/SmsSendingCapability"
          }
        }
      },
      "SmsSettings": {
        "type": "object",
        "required": [
          "messaging_profile_status",
          "webhook_config_version",
          "opt_out_config_version",
          "help_message",
          "opt_out_message",
          "opt_in_message",
          "retention_days",
          "quiet_hours_start",
          "quiet_hours_end",
          "default_automation_enabled",
          "updated_at"
        ],
        "properties": {
          "messaging_profile_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "messaging_profile_status": {
            "type": "string"
          },
          "webhook_config_version": {
            "type": "integer"
          },
          "opt_out_config_version": {
            "type": "integer"
          },
          "help_message": {
            "type": "string"
          },
          "opt_out_message": {
            "type": "string"
          },
          "opt_in_message": {
            "type": "string"
          },
          "retention_days": {
            "type": "integer",
            "minimum": 30,
            "maximum": 3650
          },
          "quiet_hours_start": {
            "type": "string"
          },
          "quiet_hours_end": {
            "type": "string"
          },
          "default_timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "default_automation_enabled": {
            "type": "boolean"
          },
          "last_synced_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SmsSettingsUpdate": {
        "type": "object",
        "properties": {
          "help_message": {
            "type": "string",
            "maxLength": 320
          },
          "opt_out_message": {
            "type": "string",
            "maxLength": 320
          },
          "opt_in_message": {
            "type": "string",
            "maxLength": 320
          },
          "retention_days": {
            "type": "integer",
            "minimum": 30,
            "maximum": 3650
          },
          "quiet_hours_start": {
            "type": "string"
          },
          "quiet_hours_end": {
            "type": "string"
          },
          "default_timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "default_automation_enabled": {
            "type": "boolean"
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "call.pre_call",
          "call.status",
          "call.report",
          "call.analysis",
          "call.recording",
          "call.transfer.initiated",
          "call.transfer.completed",
          "call.transfer.failed",
          "imessage.received",
          "imessage.reaction_received",
          "imessage.sent",
          "imessage.delivered",
          "imessage.delivery_failed",
          "sms.received",
          "sms.accepted",
          "sms.sent",
          "sms.delivered",
          "sms.delivery_failed",
          "sms.delivery_unconfirmed",
          "sms.submission_unknown",
          "sms.opted_out",
          "sms.opted_in"
        ]
      },
      "WorkspaceWebhookEventType": {
        "type": "string",
        "enum": [
          "call.pre_call",
          "call.status",
          "call.report",
          "call.analysis",
          "call.recording",
          "call.transfer.initiated",
          "call.transfer.completed",
          "call.transfer.failed",
          "sms.received",
          "sms.accepted",
          "sms.sent",
          "sms.delivered",
          "sms.delivery_failed",
          "sms.delivery_unconfirmed",
          "sms.submission_unknown",
          "sms.opted_out",
          "sms.opted_in"
        ]
      },
      "WebhookDeliveryStatus": {
        "type": "string",
        "enum": [
          "pending",
          "delivering",
          "succeeded",
          "failed",
          "cancelled",
          "expired"
        ]
      },
      "WebhookExtractionField": {
        "type": "object",
        "required": [
          "name",
          "type",
          "description"
        ],
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean",
              "enum"
            ]
          },
          "description": {
            "type": "string"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "maxLength": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "description": "String fields only. The longest value delivered, in characters; a longer extracted value is truncated. Rejected on every other type."
          }
        }
      },
      "WebhookEndpointAuthHeaderInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "value": {
            "type": "string",
            "writeOnly": true,
            "description": "Required when creating a new secret header. Omit during update to preserve its encrypted value."
          }
        }
      },
      "WebhookEndpointWritable": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WorkspaceWebhookEventType"
            }
          },
          "allAgents": {
            "type": "boolean",
            "default": true
          },
          "agentIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 250
          },
          "filterTags": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Case-sensitive subset match. Tagged matches override untagged defaults."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "authHeaders": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/WebhookEndpointAuthHeaderInput"
            }
          },
          "timeoutMs": {
            "type": "integer",
            "minimum": 500,
            "maximum": 8000,
            "default": 4000
          },
          "signingSecretSource": {
            "type": "string",
            "enum": [
              "workspace",
              "custom"
            ],
            "default": "workspace"
          },
          "signingSecret": {
            "type": "string",
            "minLength": 8,
            "writeOnly": true,
            "description": "Required when signingSecretSource is custom. Never returned."
          },
          "extractionFields": {
            "type": "array",
            "maxItems": 30,
            "items": {
              "$ref": "#/components/schemas/WebhookExtractionField"
            }
          },
          "contentMode": {
            "type": "string",
            "enum": [
              "full",
              "metadata_only"
            ],
            "default": "full",
            "description": "Controls whether SMS webhook payloads include message text."
          }
        }
      },
      "WebhookEndpointInput": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpointWritable"
          }
        ],
        "required": [
          "name",
          "url",
          "events"
        ]
      },
      "WebhookEndpointUpdate": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpointWritable"
          }
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "name",
          "url",
          "events",
          "allAgents",
          "agentIds",
          "filterTags",
          "headers",
          "authHeaders",
          "timeoutMs",
          "signingSecretSource",
          "hasCustomSigningSecret",
          "extractionFields",
          "contentMode",
          "legacyManaged",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkspaceWebhookEventType"
            }
          },
          "allAgents": {
            "type": "boolean"
          },
          "agentIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "filterTags": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "authHeaders": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "configured"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "configured": {
                  "type": "boolean",
                  "const": true
                }
              }
            }
          },
          "timeoutMs": {
            "type": "integer"
          },
          "signingSecretSource": {
            "type": "string",
            "enum": [
              "workspace",
              "custom"
            ]
          },
          "hasCustomSigningSecret": {
            "type": "boolean"
          },
          "extractionFields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookExtractionField"
            }
          },
          "contentMode": {
            "type": "string",
            "enum": [
              "full",
              "metadata_only"
            ]
          },
          "legacyManaged": {
            "type": "boolean",
            "description": "True when synchronized from deprecated agent.webhooks fields."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "required": [
          "id",
          "eventId",
          "endpointId",
          "endpointName",
          "endpointKind",
          "endpointDeleted",
          "event",
          "sessionId",
          "agentId",
          "webhookTags",
          "status",
          "attempts",
          "httpStatus",
          "error",
          "occurredAt",
          "expiresAt",
          "deliveredAt",
          "createdAt",
          "canRedeliver"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "eventId": {
            "type": "string",
            "description": "Stable Speko or upstream-provider webhook ID."
          },
          "endpointId": {
            "type": "string"
          },
          "endpointName": {
            "type": "string"
          },
          "endpointKind": {
            "type": "string",
            "enum": [
              "workspace",
              "imessage"
            ]
          },
          "endpointDeleted": {
            "type": "boolean"
          },
          "event": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "sessionId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhookTags": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "status": {
            "$ref": "#/components/schemas/WebhookDeliveryStatus"
          },
          "attempts": {
            "type": "integer"
          },
          "httpStatus": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "deliveredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "canRedeliver": {
            "type": "boolean",
            "description": "Whether manual redelivery is currently available."
          }
        }
      },
      "WebhookDeliveryEndpointOption": {
        "type": "object",
        "required": [
          "id",
          "name",
          "kind",
          "deleted"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "workspace",
              "imessage"
            ]
          },
          "deleted": {
            "type": "boolean"
          }
        }
      },
      "WebhookDeliveryPage": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "endpointOptions"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "endpointOptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDeliveryEndpointOption"
            }
          }
        }
      },
      "WebhookDeliveryAttempt": {
        "type": "object",
        "required": [
          "id",
          "attemptNumber",
          "trigger",
          "requestUrl",
          "requestHeaders",
          "requestBody",
          "responseStatus",
          "responseBody",
          "responseTruncated",
          "durationMs",
          "error",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "attemptNumber": {
            "type": "integer"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "automatic",
              "manual"
            ]
          },
          "requestUrl": {
            "type": "string",
            "description": "Sanitized URL with query values redacted."
          },
          "requestHeaders": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "requestBody": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "responseStatus": {
            "type": [
              "integer",
              "null"
            ]
          },
          "responseBody": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 65536
          },
          "responseTruncated": {
            "type": "boolean"
          },
          "durationMs": {
            "type": "integer"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookDeliveryDetail": {
        "type": "object",
        "description": "Includes all WebhookDelivery fields, with the numeric list-row attempts field exposed as attemptCount and attempts containing chronological attempt records.",
        "required": [
          "attemptCount",
          "requestPayload",
          "attempts"
        ],
        "properties": {
          "attemptCount": {
            "type": "integer"
          },
          "requestPayload": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "attempts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDeliveryAttempt"
            }
          }
        },
        "additionalProperties": true
      },
      "WebhookRedeliveryResult": {
        "type": "object",
        "required": [
          "delivered",
          "httpStatus",
          "error"
        ],
        "properties": {
          "delivered": {
            "type": "boolean"
          },
          "httpStatus": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "BulkWebhookRedeliveryRequest": {
        "type": "object",
        "required": [
          "from",
          "to"
        ],
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "Inclusive lower bound on the delivery's creation time."
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "Inclusive upper bound on the delivery's creation time. Must not be before `from`."
          },
          "endpointId": {
            "type": "string",
            "format": "uuid",
            "description": "Only deliveries to this endpoint."
          },
          "event": {
            "$ref": "#/components/schemas/WorkspaceWebhookEventType"
          },
          "status": {
            "type": "string",
            "enum": [
              "failed",
              "succeeded",
              "pending"
            ],
            "default": "failed",
            "description": "Only deliveries currently in this status."
          },
          "cursor": {
            "type": "string",
            "description": "`nextCursor` from the previous call."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "default": 50,
            "description": "Most deliveries attempted in this call."
          }
        }
      },
      "BulkWebhookRedeliveryResult": {
        "type": "object",
        "required": [
          "data",
          "count",
          "truncated",
          "nextCursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "delivered",
                "httpStatus",
                "error"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "delivered": {
                  "type": "boolean"
                },
                "httpStatus": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "error": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "`delivery_claim_failed` when another attempt was already in flight."
                }
              }
            }
          },
          "count": {
            "type": "integer",
            "description": "Deliveries attempted in this call."
          },
          "truncated": {
            "type": "boolean",
            "description": "More deliveries match past this call's last one."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "RoutingIntent": {
        "type": "object",
        "required": [
          "language"
        ],
        "properties": {
          "language": {
            "type": "string",
            "pattern": "^[a-z]{2}(-[A-Z]{2})?$",
            "description": "BCP-47 language tag, e.g. `en`, `en-US`, `es-MX`.",
            "examples": [
              "en-US"
            ]
          },
          "region": {
            "type": "string",
            "enum": [
              "global",
              "us-east4",
              "europe-west3",
              "asia-southeast1"
            ],
            "default": "global",
            "description": "Region whose streaming-latency measurements should be used. `global` falls through to batch-mode rows when no per-region data matches."
          },
          "optimizeFor": {
            "type": "string",
            "enum": [
              "balanced",
              "accuracy",
              "latency",
              "cost"
            ],
            "default": "balanced"
          }
        }
      },
      "PipelineConstraints": {
        "type": "object",
        "properties": {
          "allowedProviders": {
            "type": "object",
            "description": "Restrict candidate pool per modality. Router still ranks by score. Each entry is either `\"<vendor>\"` (vendor wildcard — allow any model from that vendor) or `\"<vendor>:<model>\"` (allow only that specific model). Failover stays active across all entries in the layer.",
            "properties": {
              "stt": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed STT provider entry. Either a vendor id (e.g. `\"deepgram\"` — any Deepgram model) or `\"<vendor>:<model>\"` (e.g. `\"deepgram:nova-3\"` — only Nova-3, no fallback to other Deepgram models within the vendor). Both forms can be mixed in the same array.",
                  "examples": [
                    "deepgram",
                    "deepgram:nova-3",
                    "assemblyai"
                  ]
                }
              },
              "llm": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed LLM provider entry. Either a vendor id (e.g. `\"openai\"` — any OpenAI model) or `\"<vendor>:<model>\"` (e.g. `\"openai:gpt-5\"` — only that model). Both forms can be mixed in the same array.",
                  "examples": [
                    "openai",
                    "openai:gpt-5",
                    "anthropic"
                  ]
                }
              },
              "tts": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed TTS provider entry. Either a vendor id (e.g. `\"elevenlabs\"` — any ElevenLabs model) or `\"<vendor>:<model>\"` (e.g. `\"elevenlabs:eleven_flash_v2_5\"` — only that model). Both forms can be mixed in the same array.",
                  "examples": [
                    "elevenlabs",
                    "elevenlabs:eleven_flash_v2_5",
                    "cartesia"
                  ]
                }
              },
              "s2s": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed S2S provider entry. Either a vendor id (e.g. `\"openai\"` — any OpenAI realtime model) or `\"<vendor>:<model>\"` (e.g. `\"openai:gpt-realtime-2.1\"` — only that model). Both forms can be mixed in the same array.",
                  "examples": [
                    "openai",
                    "openai:gpt-realtime-2.1",
                    "google:gemini-3.1-flash-live-preview",
                    "xai:grok-voice-latest"
                  ]
                }
              }
            }
          }
        }
      },
      "JsonObject": {
        "type": "object",
        "additionalProperties": true
      },
      "E164": {
        "type": "string",
        "pattern": "^\\+[1-9]\\d{6,14}$",
        "examples": [
          "+12015551234"
        ]
      },
      "TelephonyOptions": {
        "type": "object",
        "properties": {
          "region": {
            "type": "string",
            "description": "Optional SIP routing region hint forwarded as `X-Speko-Region`."
          },
          "amd": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "agent",
                  "carrier",
                  "disabled"
                ],
                "default": "agent"
              },
              "timeoutSeconds": {
                "type": "integer",
                "minimum": 1,
                "maximum": 60
              }
            }
          },
          "dtmf": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^$|^[0-9*#wW,]*[0-9*#][0-9*#wW,]*$",
            "description": "Keys to press after the far end answers, before the agent's first turn: a phone-menu path you already know, such as `ww1w8`. Digits 0-9, `*` and `#`, with `w` or `,` for a half-second pause. The agent hears nothing and says nothing until the last key is pressed, on cascade and speech-to-speech (`runMode: s2s`) calls alike. Overrides the agent's saved `outboundDtmf`; `\"\"` presses nothing on this call. Keys after the number in `to` (`+12015551234,,1`) are rejected with 400 `DTMF_IN_PHONE_NUMBER`.",
            "example": "ww1w8"
          }
        }
      },
      "VoiceDialRequest": {
        "type": "object",
        "required": [
          "to"
        ],
        "properties": {
          "to": {
            "$ref": "#/components/schemas/E164"
          },
          "from": {
            "$ref": "#/components/schemas/E164"
          },
          "agentId": {
            "type": "string",
            "description": "Persisted agent to run. Required unless `intent` is supplied."
          },
          "useDraft": {
            "type": "boolean",
            "description": "Dial with the agent's unpublished draft instead of its deployed version, to hear a change before you ship it. Only applies with `agentId`."
          },
          "agentVersion": {
            "type": "integer",
            "minimum": 1,
            "description": "Dial with this numbered version of the agent instead of its deployed one: the `version_number` from `GET /v1/agents/{id}/versions`, live, archived, or draft. The call uses that version's saved config (prompt, voice, greetings, stack, turn handling, language routing) and its workflow graph. Agent settings that versions do not record, such as tools, knowledge bases, webhooks, and ring timeout, use their current values. Requires `agentId`; cannot be combined with `useDraft`. An unknown number returns 404 `AGENT_VERSION_NOT_FOUND`."
          },
          "runMode": {
            "type": "string",
            "enum": [
              "cascade",
              "s2s"
            ],
            "description": "Run mode for this call. `s2s` hosts the call on the default speech-to-speech model (GPT-Live) instead of the STT → LLM → TTS cascade; `cascade` forces the cascade. Overrides the agent's saved run mode, and is the only way a call without an agent can run speech-to-speech. If the speech-to-speech route cannot be hosted, the call runs the cascade and records an `s2s.fallback` event; it never fails with a 4xx."
          },
          "intent": {
            "$ref": "#/components/schemas/RoutingIntent"
          },
          "constraints": {
            "$ref": "#/components/schemas/PipelineConstraints"
          },
          "voice": {
            "type": "string"
          },
          "systemPrompt": {
            "type": "string",
            "description": "Replaces the agent's system prompt for this call. Compiled as a template when `variables` is sent."
          },
          "firstMessage": {
            "type": "string",
            "description": "Replaces the agent's opening line for this call. Compiled as a template when `variables` is sent."
          },
          "variables": {
            "$ref": "#/components/schemas/TemplateVariables",
            "description": "Per-call values for `{{name}}` placeholders in the prompt and greeting. Strings, up to 10,000 characters each. Sending this key makes an unresolved variable a 400 `MISSING_TEMPLATE_VARIABLES`."
          },
          "toolSecrets": {
            "$ref": "#/components/schemas/ToolSecrets",
            "description": "Per-call values only tools can read, such as an access token. Never reach the prompt or transcript. Each value at least 6 characters."
          },
          "timezone": {
            "$ref": "#/components/schemas/CallTimeZone",
            "description": "IANA time zone for this call. Sets the agent's clock and `{{system.now}}`; overrides the agent's saved `timezone`."
          },
          "llm": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "ttsOptions": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "sttOptions": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "telephony": {
            "$ref": "#/components/schemas/TelephonyOptions"
          },
          "turnHandling": {
            "$ref": "#/components/schemas/PhoneTurnHandling"
          },
          "webhookTags": {
            "type": "object",
            "maxProperties": 20,
            "additionalProperties": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Case-sensitive routing tags for workspace webhook endpoints. Requires agentId."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Your own key/value pairs, stored on the session and echoed back in session reads, call reports, and webhook payloads. Opaque to Speko: it never reaches the agent's prompt and overrides nothing. Use `variables` for values the agent should say or use, and `toolSecrets` for values only tools should see."
          },
          "maxDurationSeconds": {
            "type": "integer",
            "default": 3600,
            "description": "Hard cap on the call's duration in seconds, measured from session creation, so dialing and ringing count. The call is ended when the cap passes. Values outside 30–14,400 are clamped to that range, and a call hosted on a speech-to-speech model is held to 3,600."
          },
          "ringTimeoutSeconds": {
            "type": "integer",
            "minimum": 20,
            "maximum": 45,
            "default": 45,
            "description": "How long the call rings before it ends as a no-answer, in whole seconds. Counted from when the callee's phone starts ringing, or from dial if the carrier never reports ringing. A call never rings for more than 90 seconds from dial. Overrides the agent's `ringTimeoutSeconds`. A value outside 20–45 is rejected with 400 `RING_TIMEOUT_OUT_OF_RANGE`. Ringing and unanswered calls are not billed."
          }
        }
      },
      "TemplateVariables": {
        "type": "object",
        "additionalProperties": {
          "type": "string",
          "maxLength": 10000
        },
        "description": "Call-time values for `{{name}}` placeholders in `systemPrompt`, `firstMessage`, and `turnHandling.voicemailMessage` (yours or the agent's). Values are strings of up to 10,000 characters. Sending this key, even as `{}`, compiles those strings as Liquid templates when the session is created: `{{name}}` interpolation, `{% if %}` branching, `| default:` filters, and the reserved `system.*` values (`system.now`, `system.call_id`, `system.caller_number`). Each variable resolves from this map, then the agent's variable-registry default, then an inline `| default:`. If any stay unresolved, the request fails with 400 `MISSING_TEMPLATE_VARIABLES` and lists the names. Keys under `system.` fail with 400 `RESERVED_VARIABLE_NAMESPACE`. Omit the key to send the strings as written; an agent with registry entries still renders, but an unbound variable renders blank instead of failing. Values reach the model, so never put tokens here — use `toolSecrets`.",
        "example": {
          "customer_name": "Priya",
          "appointment_time": "Thursday at 1 PM"
        }
      },
      "ToolSecrets": {
        "type": "object",
        "maxProperties": 32,
        "propertyNames": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
          "maxLength": 64
        },
        "additionalProperties": {
          "type": "string",
          "minLength": 6,
          "description": "At most 4,096 UTF-8 bytes."
        },
        "description": "Per-call values for tools only, such as a short-lived access token scoped to the person being called. They never enter the prompt, the model's context, the transcript, or the worker's dispatch metadata, and no read endpoint returns them. They are stored encrypted and released only to tool execution: custom-code tools read `session.secrets.<name>`, and webhook tools can reference `{{name}}` in their `url` and `headers`. Names are identifiers (`[A-Za-z_][A-Za-z0-9_]*`, up to 64 characters). Up to 32 entries; each value is at least 6 characters and at most 4,096 bytes, and all values together at most 16,384 bytes. Values shorter than 6 characters are rejected because they cannot be redacted from tool output; put short non-secret values in `variables`.",
        "example": {
          "access_token": "eyJhbGciOiJIUzI1NiJ9.example"
        }
      },
      "CallTimeZone": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64,
        "description": "IANA time zone for this call, for example `America/Chicago`. Sets the current date and time the agent schedules from and the value of `{{system.now}}`, so \"Thursday at 1 PM\" resolves to the right date. Overrides the agent's saved `timezone` for this call only. Omit to use the agent's. An unknown zone fails validation.",
        "example": "America/Chicago"
      },
      "VoiceDialResponse": {
        "type": "object",
        "required": [
          "sessionId",
          "callControlId",
          "roomName",
          "status",
          "to",
          "from"
        ],
        "properties": {
          "sessionId": {
            "type": "string"
          },
          "callControlId": {
            "type": "string",
            "description": "LiveKit SIP participant identity for the outbound leg."
          },
          "roomName": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "dialing",
              "dialing-stub"
            ]
          },
          "to": {
            "$ref": "#/components/schemas/E164"
          },
          "from": {
            "$ref": "#/components/schemas/E164"
          }
        }
      },
      "PhoneNumberDirection": {
        "type": "string",
        "enum": [
          "inbound",
          "outbound",
          "both"
        ]
      },
      "PhoneNumberSetupStatus": {
        "type": "object",
        "required": [
          "status",
          "inboundReady",
          "outboundReady",
          "agentReady",
          "forwardingRequired",
          "forwardingSipUri",
          "sipConnectionReady",
          "issues"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ready",
              "action_required",
              "suspended"
            ]
          },
          "inboundReady": {
            "type": "boolean"
          },
          "outboundReady": {
            "type": "boolean"
          },
          "agentReady": {
            "type": "boolean"
          },
          "forwardingRequired": {
            "type": "boolean"
          },
          "forwardingSipUri": {
            "type": "string",
            "nullable": true,
            "description": "sip:<E.164>@<host> your carrier must forward inbound calls to when forwardingRequired is true; null otherwise."
          },
          "sipConnectionReady": {
            "type": "boolean"
          },
          "issues": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PhoneNumber": {
        "type": "object",
        "required": [
          "id",
          "organizationId",
          "e164",
          "source",
          "direction",
          "setupStatus",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "organizationId": {
            "type": "string"
          },
          "e164": {
            "$ref": "#/components/schemas/E164"
          },
          "source": {
            "type": "string",
            "enum": [
              "managed",
              "sip_trunk"
            ]
          },
          "providerResourceId": {
            "type": "string",
            "nullable": true
          },
          "telnyxPhoneNumberId": {
            "type": "string",
            "nullable": true,
            "deprecated": true
          },
          "sipTrunkId": {
            "type": "string",
            "nullable": true,
            "deprecated": true
          },
          "sipConnectionInstallationId": {
            "type": "string",
            "nullable": true
          },
          "sipProviderName": {
            "type": "string",
            "nullable": true
          },
          "direction": {
            "$ref": "#/components/schemas/PhoneNumberDirection"
          },
          "dispatchMetadataTemplate": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true
          },
          "agentId": {
            "type": "string",
            "nullable": true
          },
          "routeToBrokerId": {
            "type": "string",
            "nullable": true,
            "description": "Org-defined broker id whose softphone this number rings instead of an agent. Mutually exclusive with `agentId` — setting one clears the other. Requires human calling to be enabled for the workspace."
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "callerName": {
            "type": "string",
            "nullable": true,
            "description": "Outbound caller name (CNAM) listed for this number, shown by called parties' carriers that display caller names. Null when none is set."
          },
          "callerNameUpdatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When `callerName` last changed. Carriers pick up a new name within about 72 hours."
          },
          "callerNameSupported": {
            "type": "boolean",
            "description": "Whether a caller name can be set on this number: true for US local numbers bought through Speko, false for imported, toll-free and non-US numbers."
          },
          "sms10dlcProfileId": {
            "type": "string",
            "nullable": true
          },
          "smsCampaignId": {
            "type": "string",
            "nullable": true
          },
          "smsAssignmentStatus": {
            "type": "string",
            "nullable": true
          },
          "smsAssignmentUpdatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "telnyxMessagingProfileId": {
            "type": "string",
            "nullable": true
          },
          "smsMessagingProfileStatus": {
            "type": "string",
            "enum": [
              "pending",
              "ready",
              "failed"
            ]
          },
          "smsMessagingProfileUpdatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "smsMessagingProfileError": {
            "type": "string",
            "nullable": true
          },
          "smsAutomationEnabled": {
            "type": "boolean"
          },
          "setupStatus": {
            "$ref": "#/components/schemas/PhoneNumberSetupStatus"
          },
          "nextChargeAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastChargedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "suspendedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Effective suspension timestamp retained for older clients. Compliance takes precedence when both compliance and billing holds are active."
          },
          "billingSuspendedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "complianceSuspendedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "suspensionReason": {
            "type": "string",
            "enum": [
              "billing",
              "compliance"
            ],
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PhoneNumberCreateRequest": {
        "type": "object",
        "required": [
          "e164"
        ],
        "properties": {
          "e164": {
            "$ref": "#/components/schemas/E164"
          },
          "direction": {
            "$ref": "#/components/schemas/PhoneNumberDirection"
          },
          "dispatchMetadataTemplate": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "label": {
            "type": "string",
            "maxLength": 120
          },
          "agentId": {
            "type": "string"
          },
          "expectedUpfrontCostUsd": {
            "type": "number",
            "minimum": 0,
            "description": "Setup price the customer agreed to, from the search row. Required for a number with `aboveStandardPrice`."
          },
          "expectedMonthlyCostUsd": {
            "type": "number",
            "minimum": 0,
            "description": "Monthly price the customer agreed to, from the search row. Required for a number with `aboveStandardPrice`."
          },
          "confirmAdditionalNumber": {
            "type": "boolean",
            "description": "The owner agreed to buy another number although the workspace already has one. Required from MCP and OAuth connector clients when the workspace has a number; without it they get `409 PHONE_NUMBER_ADDITIONAL_PURCHASE_UNCONFIRMED`."
          }
        }
      },
      "PhoneNumberImportRequest": {
        "type": "object",
        "required": [
          "e164"
        ],
        "properties": {
          "e164": {
            "$ref": "#/components/schemas/E164"
          },
          "sipConnectionInstallationId": {
            "type": "string",
            "description": "Installed SIP connection integration id. Preferred for new imports."
          },
          "sipTrunkId": {
            "type": "string",
            "description": "Legacy outbound trunk id. Required when `sipConnectionInstallationId` is omitted."
          },
          "sipProviderName": {
            "type": "string"
          },
          "direction": {
            "$ref": "#/components/schemas/PhoneNumberDirection"
          },
          "dispatchMetadataTemplate": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "label": {
            "type": "string",
            "maxLength": 120
          },
          "agentId": {
            "type": "string"
          }
        }
      },
      "PhoneNumberUpdateRequest": {
        "type": "object",
        "properties": {
          "direction": {
            "$ref": "#/components/schemas/PhoneNumberDirection"
          },
          "dispatchMetadataTemplate": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true,
            "maxLength": 120
          },
          "agentId": {
            "type": "string",
            "nullable": true
          },
          "routeToBrokerId": {
            "type": "string",
            "nullable": true,
            "description": "Org-defined broker id whose softphone this number rings instead of an agent. Mutually exclusive with `agentId` — setting one clears the other. Requires human calling to be enabled for the workspace."
          },
          "smsAutomationEnabled": {
            "type": "boolean",
            "description": "Owner/admin-only opt-in for inbound SMS agent replies on this number."
          },
          "callerName": {
            "type": "string",
            "nullable": true,
            "maxLength": 200,
            "description": "Owner/admin-only. Outbound caller name (CNAM) for a US local number bought through Speko: letters, digits and spaces, at most 15 characters after trimming (runs of spaces collapse to one). Null or an empty string removes it. Sending the current name again re-applies it. Carriers pick up a change within about 72 hours; landlines with caller-name service show it, most mobile carriers do not. Free. Must be your business name: misleading caller ID is illegal under the Truth in Caller ID Act."
          }
        }
      },
      "AvailablePhoneNumber": {
        "type": "object",
        "required": [
          "e164",
          "friendlyName",
          "monthlyCostUsd",
          "upfrontCostUsd",
          "features",
          "region"
        ],
        "properties": {
          "e164": {
            "$ref": "#/components/schemas/E164"
          },
          "friendlyName": {
            "type": "string"
          },
          "monthlyCostUsd": {
            "type": "number",
            "description": "Monthly rental this number is billed at: $2, or the carrier's monthly price when higher."
          },
          "upfrontCostUsd": {
            "type": "number",
            "description": "One-time setup this number is billed at: $1, or the carrier's upfront price when higher."
          },
          "aboveStandardPrice": {
            "type": "boolean",
            "description": "True when either price is above the standard $1 setup / $2 a month. Buying it requires `expectedUpfrontCostUsd` and `expectedMonthlyCostUsd`."
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "region": {
            "$ref": "#/components/schemas/JsonObject"
          }
        }
      },
      "PhoneNumberKybBusinessProfile": {
        "type": "object",
        "required": [
          "legalName",
          "displayName",
          "entityType",
          "country",
          "website",
          "address",
          "useCase",
          "expectedUsage"
        ],
        "properties": {
          "legalName": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "entityType": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "registrationId": {
            "type": "string"
          },
          "website": {
            "type": "string",
            "format": "uri"
          },
          "address": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "useCase": {
            "type": "string"
          },
          "expectedUsage": {
            "type": "string"
          }
        }
      },
      "PhoneNumberKybAuthorizedRepresentative": {
        "type": "object",
        "required": [
          "name",
          "title",
          "email"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "$ref": "#/components/schemas/E164"
          }
        }
      },
      "PhoneNumberKybDraftRequest": {
        "type": "object",
        "required": [
          "businessProfile",
          "authorizedRepresentative"
        ],
        "properties": {
          "businessProfile": {
            "$ref": "#/components/schemas/PhoneNumberKybBusinessProfile"
          },
          "authorizedRepresentative": {
            "$ref": "#/components/schemas/PhoneNumberKybAuthorizedRepresentative"
          },
          "attestationAccepted": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "PhoneNumberKybDeclaration": {
        "type": "object",
        "required": [
          "businessName",
          "useCase"
        ],
        "properties": {
          "businessName": {
            "type": "string",
            "maxLength": 180
          },
          "useCase": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "PhoneNumberKybAttestor": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "kind",
              "userId",
              "name",
              "email"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "user"
                ]
              },
              "userId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "organizationRole": {
                "type": "string",
                "nullable": true
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind",
              "apiKeyId"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "api_key"
                ]
              },
              "apiKeyId": {
                "type": "string"
              }
            }
          }
        ]
      },
      "PhoneNumberKybAttestationContract": {
        "type": "object",
        "required": [
          "version",
          "text",
          "termsVersion",
          "termsUrl"
        ],
        "properties": {
          "version": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "termsVersion": {
            "type": "string"
          },
          "termsUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PhoneNumberKybMinimalSubmitRequest": {
        "type": "object",
        "required": [
          "declaration",
          "attestationAccepted",
          "attestationVersion"
        ],
        "properties": {
          "declaration": {
            "$ref": "#/components/schemas/PhoneNumberKybDeclaration"
          },
          "attestationAccepted": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "attestationVersion": {
            "type": "string"
          }
        }
      },
      "PhoneNumberKybLegacySubmitRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PhoneNumberKybDraftRequest"
          },
          {
            "type": "object",
            "required": [
              "attestationAccepted"
            ],
            "properties": {
              "attestationAccepted": {
                "type": "boolean",
                "enum": [
                  true
                ]
              },
              "attestationVersion": {
                "type": "string"
              }
            }
          }
        ]
      },
      "PhoneNumberKybSubmitRequest": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/PhoneNumberKybMinimalSubmitRequest"
          },
          {
            "$ref": "#/components/schemas/PhoneNumberKybLegacySubmitRequest"
          }
        ]
      },
      "PhoneNumberKybSubmission": {
        "type": "object",
        "required": [
          "id",
          "organizationId",
          "status",
          "attestationAccepted",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "organizationId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "submitted",
              "approved",
              "rejected",
              "revoked"
            ]
          },
          "businessProfile": {
            "$ref": "#/components/schemas/PhoneNumberKybBusinessProfile"
          },
          "authorizedRepresentative": {
            "$ref": "#/components/schemas/PhoneNumberKybAuthorizedRepresentative"
          },
          "declaration": {
            "$ref": "#/components/schemas/PhoneNumberKybDeclaration",
            "nullable": true
          },
          "attestor": {
            "$ref": "#/components/schemas/PhoneNumberKybAttestor",
            "nullable": true
          },
          "attestationAccepted": {
            "type": "boolean"
          },
          "attestationVersion": {
            "type": "string",
            "nullable": true
          },
          "attestationText": {
            "type": "string",
            "nullable": true
          },
          "termsVersion": {
            "type": "string",
            "nullable": true
          },
          "attestedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "accessHoldAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "accessHoldReason": {
            "type": "string",
            "enum": [
              "rejected",
              "revoked"
            ],
            "nullable": true
          },
          "submittedByUserId": {
            "type": "string",
            "nullable": true
          },
          "submittedByEmail": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "submittedByApiKeyId": {
            "type": "string",
            "nullable": true
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "reviewerUserId": {
            "type": "string",
            "nullable": true
          },
          "reviewerEmail": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "reviewedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rejectionReason": {
            "type": "string",
            "nullable": true
          },
          "slackNotificationStatus": {
            "type": "string",
            "enum": [
              "not_queued",
              "queued",
              "enqueue_failed"
            ]
          },
          "slackNotificationJobId": {
            "type": "string",
            "nullable": true
          },
          "slackNotificationError": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PhoneNumberKybOverview": {
        "type": "object",
        "required": [
          "status",
          "submission",
          "declarationPrefill",
          "requiredAttestation",
          "attestationRequired",
          "complianceAccess",
          "prefill"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "missing",
              "draft",
              "submitted",
              "approved",
              "rejected",
              "revoked"
            ]
          },
          "submission": {
            "$ref": "#/components/schemas/PhoneNumberKybSubmission",
            "nullable": true
          },
          "declarationPrefill": {
            "$ref": "#/components/schemas/PhoneNumberKybDeclaration"
          },
          "requiredAttestation": {
            "$ref": "#/components/schemas/PhoneNumberKybAttestationContract"
          },
          "attestationRequired": {
            "type": "boolean"
          },
          "complianceAccess": {
            "type": "string",
            "enum": [
              "enabled",
              "awaiting_attestation",
              "suspended"
            ]
          },
          "prefill": {
            "type": "object",
            "nullable": true,
            "properties": {
              "businessProfile": {
                "$ref": "#/components/schemas/PhoneNumberKybBusinessProfile"
              },
              "authorizedRepresentative": {
                "$ref": "#/components/schemas/PhoneNumberKybAuthorizedRepresentative"
              }
            }
          }
        }
      },
      "AgentCallListEntry": {
        "type": "object",
        "required": [
          "id",
          "call_id",
          "resource_uri",
          "agent_id",
          "status",
          "kind",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "call_id": {
            "type": "string"
          },
          "resource_uri": {
            "type": "string"
          },
          "agent_id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "room_name": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "duration_seconds": {
            "type": "integer",
            "nullable": true
          },
          "recording_status": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AgentCallListPage": {
        "type": "object",
        "required": [
          "calls",
          "entries",
          "next_cursor"
        ],
        "properties": {
          "calls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentCallListEntry"
            }
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentCallListEntry"
            }
          },
          "next_cursor": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "AgentAnalysisPage": {
        "type": "object",
        "required": [
          "fields",
          "rows",
          "page",
          "has_more",
          "extraction_conflicts"
        ],
        "properties": {
          "fields": {
            "type": "array",
            "description": "The agent's declared extraction schema — the table's columns, in order.",
            "items": {
              "$ref": "#/components/schemas/AgentExtractionField"
            }
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentAnalysisRow"
            }
          },
          "page": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          },
          "extraction_conflicts": {
            "type": "array",
            "description": "Fields declared more than once with different definitions. The first declaration wins (the agent's own beats a webhook's); the loser is reported here rather than failing the call report.",
            "items": {
              "$ref": "#/components/schemas/AgentExtractionConflict"
            }
          }
        }
      },
      "AgentAnalysisRow": {
        "type": "object",
        "required": [
          "call_id",
          "created_at",
          "status",
          "outcome",
          "custom_data"
        ],
        "properties": {
          "call_id": {
            "type": "string"
          },
          "resource_uri": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "duration_seconds": {
            "type": "integer",
            "nullable": true
          },
          "summary": {
            "type": "string"
          },
          "outcome": {
            "type": "string"
          },
          "analysis_status": {
            "type": "string",
            "enum": [
              "heuristic",
              "completed",
              "failed"
            ]
          },
          "custom_data": {
            "description": "The extracted values for this call, keyed by field name. A field the call gave no basis for is absent.",
            "$ref": "#/components/schemas/JsonObject"
          }
        }
      },
      "AgentExtractionField": {
        "type": "object",
        "required": [
          "name",
          "type",
          "description"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "A valid identifier; the key the value lands under in `custom_data`."
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean",
              "enum"
            ]
          },
          "description": {
            "type": "string",
            "description": "The instruction the analysis LLM follows to pull this value from the transcript."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Allowed values. Required on `enum` fields, rejected on every other type."
          },
          "maxLength": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "description": "String fields only. The longest value delivered, in characters; a longer extracted value is truncated. Rejected on every other type."
          }
        }
      },
      "AgentExtractionConflict": {
        "type": "object",
        "required": [
          "name",
          "keptFrom",
          "ignoredFrom"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "keptFrom": {
            "type": "string",
            "description": "`agent`, or `endpoint:<id>`."
          },
          "ignoredFrom": {
            "type": "string",
            "description": "`agent`, or `endpoint:<id>`."
          }
        }
      },
      "CallTranscriptEntry": {
        "type": "object",
        "required": [
          "id",
          "index",
          "source",
          "text",
          "started_at",
          "metadata"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "index": {
            "type": "integer"
          },
          "source": {
            "type": "string",
            "enum": [
              "user",
              "agent",
              "system"
            ]
          },
          "text": {
            "type": "string"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "provider": {
            "type": "string",
            "nullable": true
          },
          "model": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          }
        }
      },
      "CallReportWebhookDelivery": {
        "type": "object",
        "required": [
          "endpointId",
          "deliveryId",
          "eventId",
          "delivered",
          "status",
          "error",
          "createdAt"
        ],
        "properties": {
          "endpointId": {
            "type": "string",
            "format": "uuid"
          },
          "deliveryId": {
            "type": "string",
            "format": "uuid"
          },
          "eventId": {
            "type": "string",
            "format": "uuid"
          },
          "delivered": {
            "type": "boolean"
          },
          "status": {
            "type": "integer",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CallLanguageSwitch": {
        "type": "object",
        "required": [
          "at_ms",
          "from",
          "to",
          "method"
        ],
        "properties": {
          "at_ms": {
            "type": "integer",
            "description": "Milliseconds after `session_created_at`."
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "description": "Language before the switch."
          },
          "to": {
            "type": "string",
            "description": "Language after the switch."
          },
          "method": {
            "type": [
              "string",
              "null"
            ],
            "description": "How the switch was decided, as the language router reports it."
          }
        }
      },
      "CallDtmfKey": {
        "type": "object",
        "required": [
          "actor",
          "key",
          "at_ms"
        ],
        "properties": {
          "actor": {
            "type": "string",
            "enum": [
              "agent",
              "callee"
            ],
            "description": "`agent`: the agent pressed it with `send_dtmf`. `callee`: the far end pressed it, recorded only when `turnHandling.recordCallerKeypad` is `true`."
          },
          "key": {
            "type": "string",
            "description": "One key: `0`-`9`, `*`, `#`, `A`-`D`."
          },
          "at_ms": {
            "type": "integer",
            "description": "Milliseconds after `session_created_at`."
          }
        }
      },
      "SessionDetail": {
        "type": "object",
        "description": "One session with its usage, ledger, the facts recorded on the line, and its post-call report.",
        "required": [
          "id",
          "status",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "Lifecycle status of the session."
          },
          "kind": {
            "type": "string"
          },
          "roomName": {
            "type": [
              "string",
              "null"
            ]
          },
          "providerSessionId": {
            "type": [
              "string",
              "null"
            ]
          },
          "phoneCall": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "callControlId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "phoneNumberId": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "language": {
            "type": "string",
            "description": "Configured language, or `unknown`."
          },
          "pipelineConfig": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "endReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the session closed. Same vocabulary as `endReason` on `GET /v1/sessions`."
          },
          "durationSeconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "answered": {
            "type": "boolean",
            "description": "The carrier or the SIP leg recorded an answer. Browser sessions have no phone leg and read `false`."
          },
          "answeredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the call was answered."
          },
          "ringDurationMs": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How long an outbound call rang before it was answered. Null when it was never answered or for inbound calls."
          },
          "sipCode": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The far end's final SIP response on a dial that never connected (486 busy, 603 declined, 404 no such number). Null on an answered call."
          },
          "agentVersion": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The agent version the session ran."
          },
          "languageSwitches": {
            "type": "array",
            "description": "Mid-call language changes, in order. `atMs` counts from `createdAt`.",
            "items": {
              "type": "object",
              "properties": {
                "atMs": {
                  "type": "integer"
                },
                "from": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "to": {
                  "type": "string"
                },
                "method": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "dtmf": {
            "type": "array",
            "description": "Every keypad key pressed on the call, in order. `atMs` counts from `createdAt`. Keys the caller pressed appear only when the agent's `turnHandling.recordCallerKeypad` is `true`; otherwise only the agent's own keys are listed.",
            "items": {
              "type": "object",
              "properties": {
                "actor": {
                  "type": "string",
                  "enum": [
                    "agent",
                    "callee"
                  ]
                },
                "key": {
                  "type": "string"
                },
                "atMs": {
                  "type": "integer"
                }
              }
            }
          },
          "speechDetected": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true` once the caller says something that reaches the transcript. `false` only after the session ends with no such turn. Null while the session is open with nothing heard yet, and on a dialed call nobody answered."
          },
          "speechDetectedReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "silence",
              "not_recognized",
              "stt_error",
              null
            ],
            "description": "Why `speechDetected` is false."
          },
          "voicemail": {
            "type": "object",
            "description": "Machine detection and voicemail handling, the `voicemail` block of the call report in camelCase.",
            "properties": {
              "detected": {
                "type": "boolean"
              },
              "amdVerdict": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "delivery": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "amdConfidence": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Confidence (0 to 1) of the far-end reading that set `amd_verdict`. Null when the verdict came from the machine detector's own label, which carries no score, and on reports finalized before this field existed."
              },
              "amdDecidedAtMs": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Milliseconds from the answer to the event that set `amd_verdict`. Null when there is no verdict."
              }
            }
          },
          "report": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CallReport"
              },
              {
                "type": "null"
              }
            ],
            "description": "The stored post-call report: the same object `GET /v1/calls/{id}/report` returns, snake_case like that endpoint. Null until the report is finalized."
          },
          "totalCostUsd": {
            "type": "number"
          },
          "billing": {
            "type": [
              "object",
              "null"
            ]
          },
          "recordingObjectPath": {
            "type": [
              "string",
              "null"
            ]
          },
          "recordingDurationMs": {
            "type": [
              "integer",
              "null"
            ]
          },
          "recordingStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "usage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JsonObject"
            }
          },
          "ledger": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JsonObject"
            }
          }
        }
      },
      "CallReportVoicemail": {
        "type": "object",
        "description": "Machine detection and voicemail handling for the call.",
        "required": [
          "detected",
          "amd_verdict",
          "delivery"
        ],
        "properties": {
          "detected": {
            "type": "boolean",
            "description": "A recordable mailbox was reached."
          },
          "amd_verdict": {
            "type": [
              "string",
              "null"
            ],
            "description": "Latest machine-detection verdict for the line: `human`, `machine-vm`, `machine-ivr`, `machine-unavailable` or `unknown`. `null` when detection did not run."
          },
          "delivery": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "delivered",
              "missing_message",
              "greeting_timeout",
              "playout_failed",
              "call_ended",
              "not_attempted",
              null
            ],
            "description": "Result of `onMachine: leave_message`. `call_ended`: the far end hung up while the message played. `not_attempted`: the call ended before the message started. `null` when no message was owed."
          },
          "amd_confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence (0 to 1) of the far-end reading that set `amd_verdict`. Null when the verdict came from the machine detector's own label, which carries no score, and on reports finalized before this field existed."
          },
          "amd_decided_at_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Milliseconds from the answer to the event that set `amd_verdict`. Null when there is no verdict."
          }
        }
      },
      "CallReport": {
        "type": "object",
        "required": [
          "session_id",
          "summary",
          "outcome",
          "transcript",
          "cost_micro_usd",
          "webhook_deliveries"
        ],
        "properties": {
          "session_id": {
            "type": "string"
          },
          "organization_id": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "outcome": {
            "type": "string"
          },
          "contact": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "connected",
              "no_answer",
              "not_connected",
              "no_response",
              null
            ],
            "description": "Whether anybody reached anybody on the call, from what was recorded on the line (the session's status and end reason, the carrier's answer, who spoke), never from the analysis: `connected` (the caller said something, or a voicemail or unavailable mailbox answered), `no_answer` (the line rang and nobody picked up), `not_connected` (the dial was refused, or nobody said a word), `no_response` (the agent spoke and nobody replied). `null` while the call is open and on reports finalized before this field existed. `outcome` is unchanged: on a call with no conversation it can still read `completed`, so read `contact` to tell those calls apart."
          },
          "end_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the call ended, in the vocabulary of the session's `endReason` (see `GET /v1/sessions`). A report finalized before the session's own close can already read `voicemail_reached` from the recorded line verdict."
          },
          "session_created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the session was created. `created_at` on the `call.report` webhook is when the payload was built, not this."
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the session closed."
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Same value as `durationSeconds` on `GET /v1/sessions/{id}`. A refused dial reads 0."
          },
          "answered": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "The carrier or the SIP leg recorded an answer. Browser sessions have no phone leg and read `false`."
          },
          "answered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the call was answered."
          },
          "ring_duration_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How long an outbound call rang before it was answered. Null when it was never answered or for inbound calls."
          },
          "sip_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The far end's final SIP response on a dial that never connected (486 busy, 603 declined, 404 no such number). Null on an answered call."
          },
          "agent_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The agent version the session ran."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "description": "The language the session was configured to start in."
          },
          "language_switches": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/CallLanguageSwitch"
            },
            "description": "Mid-call language changes, in order."
          },
          "dtmf": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/CallDtmfKey"
            },
            "description": "Every keypad key pressed on the call, by the agent or the callee, in order. Callee keys appear only when the agent's `turnHandling.recordCallerKeypad` is `true`; otherwise only the agent's own keys are listed."
          },
          "speech_detected": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true` once the caller says something that reaches the transcript. `false` only after the session ends with no such turn. Null while the session is open with nothing heard yet, and on a dialed call nobody answered."
          },
          "speech_detected_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "silence",
              "not_recognized",
              "stt_error",
              null
            ],
            "description": "Why `speech_detected` is false. `silence`: nothing was heard. `not_recognized`: speech was heard but produced no transcript. `stt_error`: the transcription stream failed. Every field above is null on reports finalized before it existed."
          },
          "structured_data": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "custom_data": {
            "description": "Caller-defined extraction values keyed by field name, matching the `call.report` webhook. `{}` when the agent declares no extraction fields.",
            "$ref": "#/components/schemas/JsonObject"
          },
          "transcript": {
            "type": "object",
            "properties": {
              "entries": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CallReportTranscriptEntry"
                }
              }
            }
          },
          "cost_micro_usd": {
            "type": "string"
          },
          "cost_breakdown": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JsonObject"
            }
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "scheduled_callback": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "voicemail": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CallReportVoicemail"
              },
              {
                "type": "null"
              }
            ],
            "description": "Machine detection and voicemail handling for the call, matching the `call.report` webhook. `null` on reports finalized before this field existed."
          },
          "analysis_status": {
            "type": "string",
            "enum": [
              "heuristic",
              "completed",
              "failed"
            ]
          },
          "post_call_webhook_status": {
            "type": "string",
            "deprecated": true,
            "description": "Deprecated aggregate. Use webhook_deliveries."
          },
          "post_call_webhook_attempts": {
            "type": "integer",
            "deprecated": true,
            "description": "Deprecated aggregate. Use webhook_deliveries."
          },
          "post_call_webhook_next_retry_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "deprecated": true,
            "description": "Deprecated aggregate. Use webhook_deliveries."
          },
          "post_call_webhook_delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "deprecated": true,
            "description": "Deprecated aggregate. Use webhook_deliveries."
          },
          "post_call_webhook_error": {
            "type": "string",
            "nullable": true,
            "deprecated": true,
            "description": "Deprecated aggregate. Use webhook_deliveries."
          },
          "webhook_deliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CallReportWebhookDelivery"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CallTransfer": {
        "type": "object",
        "required": [
          "id",
          "session_id",
          "organization_id",
          "kind",
          "status",
          "transfer_to",
          "metadata",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "session_id": {
            "type": "string"
          },
          "organization_id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "blind",
              "warm"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "requested",
              "screening",
              "bridging",
              "completed",
              "failed",
              "cancelled"
            ]
          },
          "transfer_to": {
            "type": "string"
          },
          "from_room_name": {
            "type": "string",
            "nullable": true
          },
          "consultation_room_name": {
            "type": "string",
            "nullable": true
          },
          "caller_participant_identity": {
            "type": "string",
            "nullable": true
          },
          "recipient_participant_identity": {
            "type": "string",
            "nullable": true
          },
          "outbound_trunk_id": {
            "type": "string",
            "nullable": true
          },
          "screening_prompt": {
            "type": "string",
            "nullable": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "failure_cause": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "CallTransferResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CallTransfer"
          },
          {
            "type": "object",
            "properties": {
              "routing_attempts": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CallTransfer"
                }
              },
              "next_transfer": {
                "$ref": "#/components/schemas/CallTransfer"
              },
              "fallback": {
                "$ref": "#/components/schemas/JsonObject"
              }
            }
          }
        ]
      },
      "CallDetail": {
        "type": "object",
        "required": [
          "id",
          "call_id",
          "resource_uri",
          "status",
          "kind",
          "created_at",
          "transcript"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "call_id": {
            "type": "string"
          },
          "resource_uri": {
            "type": "string"
          },
          "agent_id": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "room_name": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string"
          },
          "pipeline_config": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "end_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the call ended, in the vocabulary of the session's `endReason`. Null while live."
          },
          "duration_seconds": {
            "type": "integer",
            "nullable": true
          },
          "recording_status": {
            "type": "string",
            "nullable": true
          },
          "recording_duration_ms": {
            "type": "integer",
            "nullable": true
          },
          "recording_resource_uri": {
            "type": "string"
          },
          "report": {
            "$ref": "#/components/schemas/CallReport"
          },
          "transfers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CallTransfer"
            }
          },
          "transcript": {
            "type": "object",
            "properties": {
              "entries": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CallTranscriptEntry"
                }
              }
            }
          },
          "span_tree": {
            "$ref": "#/components/schemas/JsonObject"
          }
        }
      },
      "CallEvent": {
        "type": "object",
        "required": [
          "id",
          "organization_id",
          "provider",
          "event_type",
          "occurred_at",
          "payload",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "session_id": {
            "type": "string",
            "nullable": true
          },
          "organization_id": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "nullable": true
          },
          "failure_cause": {
            "type": "string",
            "nullable": true
          },
          "sip_status_code": {
            "type": "integer",
            "nullable": true
          },
          "sip_status": {
            "type": "string",
            "nullable": true
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "payload": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CallEventsResponse": {
        "type": "object",
        "required": [
          "events"
        ],
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CallEvent"
            }
          }
        }
      },
      "FinalizeCallReportRequest": {
        "type": "object",
        "properties": {
          "forceAnalysis": {
            "type": "boolean"
          },
          "retryWebhook": {
            "type": "boolean"
          }
        }
      },
      "FinalizeCallReportResponse": {
        "type": "object",
        "required": [
          "session_id",
          "summary",
          "outcome",
          "cost_micro_usd",
          "webhook",
          "webhook_deliveries"
        ],
        "properties": {
          "session_id": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "outcome": {
            "type": "string"
          },
          "contact": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "connected",
              "no_answer",
              "not_connected",
              "no_response",
              null
            ],
            "description": "Whether anybody reached anybody on the call, from what was recorded on the line (the session's status and end reason, the carrier's answer, who spoke), never from the analysis: `connected` (the caller said something, or a voicemail or unavailable mailbox answered), `no_answer` (the line rang and nobody picked up), `not_connected` (the dial was refused, or nobody said a word), `no_response` (the agent spoke and nobody replied). `null` while the call is open and on reports finalized before this field existed. `outcome` is unchanged: on a call with no conversation it can still read `completed`, so read `contact` to tell those calls apart."
          },
          "cost_micro_usd": {
            "type": "string"
          },
          "webhook": {
            "$ref": "#/components/schemas/JsonObject",
            "deprecated": true,
            "description": "Deprecated aggregate. Use webhook_deliveries."
          },
          "webhook_deliveries": {
            "type": "array",
            "description": "Canonical per-endpoint delivery results. The singular webhook field is a deprecated aggregate.",
            "items": {
              "$ref": "#/components/schemas/CallReportWebhookDelivery"
            }
          }
        }
      },
      "BlindTransferRequest": {
        "type": "object",
        "required": [
          "to"
        ],
        "properties": {
          "to": {
            "type": "string"
          },
          "participantIdentity": {
            "type": "string"
          },
          "playDialtone": {
            "type": "boolean"
          },
          "ringingTimeout": {
            "type": "integer",
            "minimum": 1,
            "maximum": 120
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "WarmTransferRequest": {
        "type": "object",
        "properties": {
          "to": {
            "$ref": "#/components/schemas/E164"
          },
          "destinations": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/JsonObject"
            }
          },
          "from": {
            "$ref": "#/components/schemas/E164"
          },
          "participantIdentity": {
            "type": "string"
          },
          "outboundTrunkId": {
            "type": "string"
          },
          "screeningPrompt": {
            "type": "string"
          },
          "screening": {
            "type": "boolean",
            "description": "Set to false to skip the screening agent. The recipient joins the live call as soon as they answer, and the agent stays quiet until they leave. Defaults to true."
          },
          "summary": {
            "type": "string"
          },
          "ringingTimeout": {
            "type": "integer",
            "minimum": 1,
            "maximum": 120
          },
          "waitUntilAnswered": {
            "type": "boolean"
          },
          "fallback": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "voicemailDetection": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          }
        }
      },
      "CompleteWarmTransferRequest": {
        "type": "object",
        "properties": {
          "recipientParticipantIdentity": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          }
        }
      },
      "CancelWarmTransferRequest": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "tryNext": {
            "type": "boolean"
          },
          "voicemailDetected": {
            "type": "boolean"
          }
        }
      },
      "ScheduledCallbackStatus": {
        "type": "string",
        "enum": [
          "scheduled",
          "dispatching",
          "dispatched",
          "cancelled",
          "failed"
        ]
      },
      "ScheduledCallback": {
        "type": "object",
        "required": [
          "id",
          "organization_id",
          "to_number",
          "scheduled_at",
          "status",
          "pipeline_config",
          "metadata",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "organization_id": {
            "type": "string"
          },
          "source_session_id": {
            "type": "string",
            "nullable": true
          },
          "created_session_id": {
            "type": "string",
            "nullable": true
          },
          "agent_id": {
            "type": "string",
            "nullable": true
          },
          "phone_number_id": {
            "type": "string",
            "nullable": true
          },
          "to_number": {
            "$ref": "#/components/schemas/E164"
          },
          "from_number": {
            "type": "string",
            "nullable": true
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/ScheduledCallbackStatus"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "instructions": {
            "type": "string",
            "nullable": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "pipeline_config": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "failure_cause": {
            "type": "string",
            "nullable": true
          },
          "attempted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "dispatched_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancelled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ScheduledCallbacksResponse": {
        "type": "object",
        "required": [
          "callbacks"
        ],
        "properties": {
          "callbacks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScheduledCallback"
            }
          }
        }
      },
      "ChatToolCall": {
        "type": "object",
        "required": [
          "id",
          "name",
          "args"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "args": {
            "type": "string",
            "description": "JSON-encoded tool arguments."
          }
        }
      },
      "ChatTool": {
        "type": "object",
        "required": [
          "name",
          "description",
          "parameters"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "JSON Schema parameters object."
          },
          "executionMode": {
            "type": "string",
            "enum": [
              "inline",
              "webhook",
              "builtin"
            ],
            "description": "Where the tool runs. Omit for inline execution."
          },
          "source": {
            "type": "object",
            "additionalProperties": true,
            "description": "Webhook, builtin, or inline execution source config."
          }
        }
      },
      "ChatMessage": {
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "system",
              "user",
              "assistant",
              "tool"
            ]
          },
          "content": {
            "type": "string"
          },
          "toolCalls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatToolCall"
            }
          },
          "toolCallId": {
            "type": "string",
            "description": "Required on `role: tool`; pairs with a prior assistant tool call id."
          },
          "isError": {
            "type": "boolean",
            "description": "Marks a tool message as an error result."
          }
        }
      },
      "CreateSessionRequest": {
        "description": "Set `mode` explicitly when possible. If omitted, the server uses the persisted agent's S2S run mode when applicable and otherwise defaults to cascade.",
        "anyOf": [
          {
            "$ref": "#/components/schemas/CreateCascadeSessionRequest"
          },
          {
            "$ref": "#/components/schemas/CreateS2SSessionRequest"
          }
        ]
      },
      "CreateCascadeSessionRequest": {
        "type": "object",
        "description": "Cascade session creation fields. Exactly one of `agentId` or `intent` must be supplied. When `agentId` is given, the persisted agent's `systemPrompt`, `voice`, `intent`, and `llmOptions` seed the session as defaults; any per-call field on this body still overrides the agent's stored value. The server defaults an omitted mode to cascade unless the persisted agent uses S2S run mode.",
        "properties": {
          "mode": {
            "type": "string",
            "const": "cascade",
            "description": "Creates the LiveKit-backed STT → LLM → TTS pipeline. Optional for backward compatibility."
          },
          "agentId": {
            "type": "string",
            "description": "Optional pointer to a persisted `agent` row whose fields seed this session. When supplied, the agent's `systemPrompt`, `voice`, `intent`, and `llmOptions` become defaults; any per-call field on this body overrides the agent's stored value. When absent, `intent` is required."
          },
          "agentVersion": {
            "type": "integer",
            "minimum": 1,
            "description": "Run this session on this numbered version of the agent instead of its deployed one: the `version_number` from `GET /v1/agents/{id}/versions`, live, archived, or draft. Uses that version's saved config and workflow graph; agent settings that versions do not record (tools, knowledge bases, webhooks) use their current values. Requires `agentId`; cannot be combined with `useDraft`. An unknown number returns 404 `AGENT_VERSION_NOT_FOUND`."
          },
          "intent": {
            "$ref": "#/components/schemas/RoutingIntent"
          },
          "constraints": {
            "$ref": "#/components/schemas/PipelineConstraints"
          },
          "voice": {
            "type": "string",
            "description": "Force a specific TTS voice id."
          },
          "systemPrompt": {
            "type": "string",
            "description": "Initial agent instructions."
          },
          "firstMessage": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "First line the agent speaks on connect. Omit to use the agent's or the default greeting; pass `null` or `\"\"` to open in listening mode."
          },
          "variables": {
            "$ref": "#/components/schemas/TemplateVariables",
            "description": "Per-call values for `{{name}}` placeholders in the prompt and greeting. Strings, up to 10,000 characters each. Sending this key makes an unresolved variable a 400 `MISSING_TEMPLATE_VARIABLES`."
          },
          "toolSecrets": {
            "$ref": "#/components/schemas/ToolSecrets",
            "description": "Per-call values only tools can read, such as an access token. Never reach the prompt or transcript. Each value at least 6 characters."
          },
          "timezone": {
            "$ref": "#/components/schemas/CallTimeZone",
            "description": "IANA time zone for this call. Sets the agent's clock and `{{system.now}}`; overrides the agent's saved `timezone`."
          },
          "llm": {
            "type": "object",
            "properties": {
              "temperature": {
                "type": "number",
                "minimum": 0,
                "maximum": 2
              },
              "maxTokens": {
                "type": "integer",
                "minimum": 1
              }
            }
          },
          "ttsOptions": {
            "type": "object",
            "properties": {
              "sampleRate": {
                "type": "integer",
                "minimum": 1
              },
              "speed": {
                "type": "number",
                "minimum": 0.5,
                "maximum": 2
              }
            }
          },
          "turnHandling": {
            "$ref": "#/components/schemas/SessionTurnHandling"
          },
          "webhookTags": {
            "type": "object",
            "maxProperties": 20,
            "additionalProperties": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Case-sensitive routing tags for workspace webhook endpoints. Requires agentId."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Arbitrary key/value pairs stored on the session row."
          },
          "ttlSeconds": {
            "type": "integer",
            "default": 900,
            "minimum": 1,
            "maximum": 86400,
            "description": "Join-token TTL. Bounds how long the returned transport token stays valid to connect — not how long the session may run. Use `maxDurationSeconds` to cap connected duration."
          },
          "maxDurationSeconds": {
            "type": "integer",
            "default": 3600,
            "minimum": 30,
            "maximum": 14400,
            "description": "Hard cap on session lifetime in seconds, measured from session creation (not from client connect). The session is force-ended when the deadline passes, even if the client stays connected — a client that joins late gets the remainder. Billing accrues per second from session creation (on an outbound phone call that includes dialing and ringing; failed sessions and carrier-refused dials are not billed), so this is the spend guardrail for abandoned clients."
          },
          "identity": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Transport participant identity. Defaults to `user_<uuid>`."
          }
        }
      },
      "CreateS2SSessionRequest": {
        "type": "object",
        "description": "Provider-direct speech-to-speech fields. Speko reserves a bounded entitlement and returns a scoped provider credential; media flows directly between the caller and the selected provider. Mode may be omitted when the persisted agent uses S2S run mode.",
        "properties": {
          "mode": {
            "type": "string",
            "const": "s2s",
            "description": "Creates a provider-direct S2S session. Optional when agentId resolves to an S2S agent."
          },
          "agentId": {
            "type": "string",
            "minLength": 1,
            "description": "Optional persisted agent whose prompt, voice, language intent, and registered tools seed the S2S session."
          },
          "agentVersion": {
            "type": "integer",
            "minimum": 1,
            "description": "Run this session on this numbered version of the agent instead of its deployed one: the `version_number` from `GET /v1/agents/{id}/versions`, live, archived, or draft. Uses that version's saved config and workflow graph; agent settings that versions do not record (tools, knowledge bases, webhooks) use their current values. Requires `agentId`; cannot be combined with `useDraft`. An unknown number returns 404 `AGENT_VERSION_NOT_FOUND`."
          },
          "intent": {
            "$ref": "#/components/schemas/RoutingIntent"
          },
          "constraints": {
            "$ref": "#/components/schemas/PipelineConstraints"
          },
          "s2s": {
            "type": "object",
            "description": "Provider-native S2S configuration. Omit both provider and model to let Speko route; if either is supplied, both are required.",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "openai",
                  "google",
                  "xai"
                ],
                "description": "Managed provider-direct S2S provider."
              },
              "model": {
                "type": "string",
                "minLength": 1,
                "description": "Exact provider model id."
              },
              "voice": {
                "type": "string",
                "description": "Provider-specific voice id."
              },
              "systemPrompt": {
                "type": "string",
                "description": "Initial provider session instructions."
              },
              "temperature": {
                "type": "number",
                "minimum": 0,
                "maximum": 2
              },
              "inputSampleRate": {
                "type": "integer",
                "enum": [
                  16000,
                  24000
                ],
                "description": "PCM16 input rate. Gemini Live requires 16 kHz; OpenAI and xAI require 24 kHz."
              },
              "outputSampleRate": {
                "type": "integer",
                "enum": [
                  16000,
                  24000
                ],
                "description": "PCM16 output rate. Current managed OpenAI, Gemini Live, and xAI routes require 24 kHz."
              },
              "tools": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RealtimeToolSpec"
                }
              }
            }
          },
          "variables": {
            "$ref": "#/components/schemas/TemplateVariables",
            "description": "Per-call values for `{{name}}` placeholders in the prompt and greeting. Strings, up to 10,000 characters each. Sending this key makes an unresolved variable a 400 `MISSING_TEMPLATE_VARIABLES`."
          },
          "firstMessage": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "First line the agent speaks on connect, for `openai` `gpt-live` models. GPT-Live paraphrases it rather than reading it word for word. Omit to use the agent's or the default greeting; pass `null` or `\"\"` to open in listening mode. Other S2S providers ignore it, and so does an agent running a GPT-Live graph, which opens from the graph's first step."
          },
          "timezone": {
            "$ref": "#/components/schemas/CallTimeZone",
            "description": "IANA time zone for this call. Sets the agent's clock and `{{system.now}}`; overrides the agent's saved `timezone`."
          },
          "webhookTags": {
            "type": "object",
            "maxProperties": 20,
            "additionalProperties": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Case-sensitive workspace webhook routing tags. Requires agentId."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Arbitrary key/value pairs stored on the session row."
          },
          "ttlSeconds": {
            "type": "integer",
            "default": 1800,
            "minimum": 1,
            "maximum": 3600,
            "description": "Requested maximum S2S session horizon. The returned entitlement or provider credential may authorize a shorter initial slice."
          }
        }
      },
      "RealtimeToolSpec": {
        "type": "object",
        "required": [
          "name",
          "description",
          "parameters"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "description": {
            "type": "string"
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "JSON Schema parameters object."
          }
        }
      },
      "CreateSessionResponse": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/CreateCascadeSessionResponse"
          },
          {
            "$ref": "#/components/schemas/CreateS2SSessionResponse"
          }
        ]
      },
      "CreateCascadeSessionResponse": {
        "type": "object",
        "required": [
          "mode",
          "sessionId",
          "transportToken",
          "transportUrl",
          "conversationToken",
          "livekitUrl",
          "roomName",
          "identity",
          "expiresAt"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "const": "cascade"
          },
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "transportToken": {
            "type": "string",
            "description": "Browser-safe media transport token."
          },
          "transportUrl": {
            "type": "string",
            "format": "uri",
            "description": "Media transport URL."
          },
          "conversationToken": {
            "type": "string",
            "deprecated": true,
            "description": "Compatibility alias for `transportToken`."
          },
          "livekitUrl": {
            "type": "string",
            "format": "uri",
            "deprecated": true,
            "description": "Compatibility alias for `transportUrl`."
          },
          "roomName": {
            "type": "string",
            "examples": [
              "speko_..."
            ]
          },
          "identity": {
            "type": "string"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateS2SSessionResponse": {
        "type": "object",
        "description": "Provider-direct S2S bootstrap. Pass this response unchanged to `RealtimeVoiceConversation.create()`; do not log or persist the credential value.",
        "required": [
          "mode",
          "transport",
          "sessionId",
          "planId",
          "attemptId",
          "provider",
          "model",
          "adapter",
          "providerTransport",
          "endpoint",
          "credential",
          "telemetry",
          "reservation",
          "session",
          "inputSampleRate",
          "outputSampleRate",
          "expiresAt"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "const": "s2s"
          },
          "transport": {
            "type": "string",
            "const": "provider_direct"
          },
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "planId": {
            "type": "string"
          },
          "attemptId": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "openai",
              "google",
              "xai"
            ]
          },
          "model": {
            "type": "string"
          },
          "adapter": {
            "type": "string",
            "enum": [
              "openai.realtime.v1",
              "google.live.v1",
              "xai.realtime.v1"
            ]
          },
          "providerTransport": {
            "type": "string",
            "enum": [
              "webrtc",
              "websocket"
            ],
            "description": "OpenAI uses WebRTC; Gemini Live and xAI use WebSockets."
          },
          "endpoint": {
            "type": "string",
            "format": "uri",
            "description": "Allowlisted provider endpoint."
          },
          "credential": {
            "type": "object",
            "required": [
              "kind",
              "value",
              "expiresAt"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "const": "bearer"
              },
              "value": {
                "type": "string",
                "description": "Scoped, short-lived provider credential. Never persist or log it."
              },
              "expiresAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "telemetry": {
            "type": "object",
            "required": [
              "endpoint",
              "token",
              "flushIntervalMs"
            ],
            "properties": {
              "endpoint": {
                "type": "string",
                "format": "uri",
                "description": "Speko control endpoint. It never receives media."
              },
              "token": {
                "type": "string",
                "description": "Short-lived token scoped to this session's control events."
              },
              "flushIntervalMs": {
                "type": "integer",
                "minimum": 1
              }
            }
          },
          "sidebandUrl": {
            "type": "string",
            "format": "uri",
            "description": "OpenAI-only Speko endpoint that binds the provider call to authenticated billing evidence without receiving media."
          },
          "reservation": {
            "type": "object",
            "required": [
              "id",
              "authorizedDurationSeconds",
              "leaseExpiresAt",
              "billing"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "authorizedDurationSeconds": {
                "type": "integer",
                "minimum": 1,
                "description": "Duration authorized by this entitlement slice."
              },
              "leaseExpiresAt": {
                "type": "string",
                "format": "date-time"
              },
              "billing": {
                "type": "object",
                "required": [
                  "mode",
                  "state",
                  "maximumAmountMicros",
                  "currency"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "const": "direct_entitlement"
                  },
                  "state": {
                    "type": "string",
                    "const": "estimated"
                  },
                  "maximumAmountMicros": {
                    "type": "string",
                    "pattern": "^[0-9]+$",
                    "description": "Maximum charge for this authorized slice, in millionths of the stated currency."
                  },
                  "currency": {
                    "type": "string",
                    "examples": [
                      "USD"
                    ]
                  },
                  "renewalUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Present when the client may reserve another entitlement slice, currently for Gemini Live."
                  },
                  "renewableUntil": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          },
          "session": {
            "type": "object",
            "properties": {
              "voice": {
                "type": "string"
              },
              "instructions": {
                "type": "string"
              },
              "temperature": {
                "type": "number"
              },
              "tools": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RealtimeToolSpec"
                }
              }
            }
          },
          "inputSampleRate": {
            "type": "integer",
            "enum": [
              16000,
              24000
            ]
          },
          "outputSampleRate": {
            "type": "integer",
            "enum": [
              24000
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Expiry of the currently returned provider credential."
          }
        }
      },
      "TranscribeResponse": {
        "type": "object",
        "required": [
          "text",
          "provider",
          "model",
          "failoverCount"
        ],
        "properties": {
          "text": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "description": "Provider that actually ran (post-failover)."
          },
          "model": {
            "type": "string"
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "failoverCount": {
            "type": "integer",
            "minimum": 0
          },
          "scoresRunId": {
            "type": [
              "string",
              "null"
            ]
          },
          "effectiveParams": {
            "type": "object",
            "description": "Present only when the request used `workload`, `providerOptions`, or a canonical STT field. Reports what actually applied on the winning provider.",
            "required": [
              "provider",
              "model",
              "applied",
              "defaultsVersion"
            ],
            "properties": {
              "provider": {
                "type": "string"
              },
              "model": {
                "type": "string"
              },
              "applied": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Param names applied to the winning provider (never values)."
              },
              "defaultsVersion": {
                "type": "string",
                "description": "Version of the param registry whose workload defaults applied."
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Present only when non-empty and the request used the new fields. Entries carry a `kind`: `candidate_excluded` (demote-class param removed a failover candidate), `param_dropped` (drop-class param omitted for the winning provider), `out_of_range`, or `provider_not_pinned`."
          }
        }
      },
      "VoiceCatalogEntry": {
        "type": "object",
        "required": [
          "vendor",
          "id",
          "name"
        ],
        "properties": {
          "vendor": {
            "type": "string",
            "description": "Routing-key vendor (matches `allowedProviders.tts` entries)."
          },
          "id": {
            "type": "string",
            "description": "Voice id passed through to the provider's TTS API."
          },
          "name": {
            "type": "string",
            "description": "Human-readable label."
          },
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The vendor's models whose voice list includes this voice. Present only where a vendor's models list different voices (Paxa). Absent means every model of the vendor speaks it."
          }
        }
      },
      "VoicesProviderEntry": {
        "type": "object",
        "required": [
          "key",
          "name",
          "models",
          "voicesFetchedLive",
          "source"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "voicesFetchedLive": {
            "type": "boolean",
            "description": "`true` when the provider's voice library is account-scoped and must be fetched live from the provider (currently ElevenLabs)."
          },
          "source": {
            "type": "string",
            "enum": [
              "BYOK",
              "MANAGED"
            ],
            "description": "Which credential routes this provider for your organization. `BYOK` when you have configured your own key for it, `MANAGED` when it rides a Speko platform key. There is no third value — a provider covered by neither is not returned at all."
          }
        }
      },
      "VoicesListResponse": {
        "type": "object",
        "required": [
          "voices",
          "providers"
        ],
        "properties": {
          "voices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VoiceCatalogEntry"
            }
          },
          "providers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VoicesProviderEntry"
            }
          }
        }
      },
      "SynthesizeRequest": {
        "type": "object",
        "required": [
          "text",
          "intent"
        ],
        "properties": {
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50000
          },
          "intent": {
            "$ref": "#/components/schemas/RoutingIntent"
          },
          "voice": {
            "type": "string",
            "description": "Provider-specific voice id. Speko falls back to a sane default per provider when omitted."
          },
          "model": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Optional upstream model name (e.g. `eleven_multilingual_v2`, `sonic-3.6`, `gpt-4o-mini-tts`, `qwen3-tts-flash`). When set, overrides the selector's chosen model on the primary candidate only — failover candidates still use the selector's model so a model intended for provider A is not sent to provider B."
          },
          "speed": {
            "type": "number",
            "minimum": 0.5,
            "maximum": 2
          },
          "workload": {
            "type": "string",
            "enum": [
              "conversation",
              "transcription",
              "narration"
            ],
            "description": "Applies per-provider defaults from the param registry (e.g. `narration` sets ElevenLabs `voice_settings` and prefers a 24 kHz ladder when `sampleRate` is unset). Explicit fields override defaults. Applied param names are reported in the `X-Speko-Applied-Params` response header."
          },
          "providerOptions": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Per-provider TTS params keyed by provider id, validated against the registry (e.g. `{\"elevenlabs\":{\"voice_settings\":{\"stability\":0.6}}}`). A provider's options apply only when that provider serves the request and never cross providers on failover. Setting a provider spelling of `speed` alongside the top-level `speed` is rejected as `canonical_collision`."
          },
          "constraints": {
            "$ref": "#/components/schemas/PipelineConstraints"
          }
        }
      },
      "CompleteRequest": {
        "type": "object",
        "required": [
          "messages",
          "intent"
        ],
        "properties": {
          "messages": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "intent": {
            "$ref": "#/components/schemas/RoutingIntent"
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2
          },
          "maxTokens": {
            "type": "integer",
            "minimum": 1
          },
          "systemPrompt": {
            "type": "string",
            "description": "Forwarded via the canonical system-prompt slot. Don't *also* prepend the same content as a `system` message."
          },
          "constraints": {
            "$ref": "#/components/schemas/PipelineConstraints"
          },
          "reasoningEffort": {
            "type": "string",
            "enum": [
              "none",
              "minimal",
              "low",
              "medium",
              "high",
              "xhigh"
            ]
          },
          "tools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatTool"
            }
          },
          "toolChoice": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "auto",
                  "none",
                  "required"
                ]
              },
              {
                "type": "object",
                "additionalProperties": true
              }
            ]
          },
          "parallelToolCalls": {
            "type": "boolean"
          },
          "maxToolHops": {
            "type": "integer",
            "minimum": 1,
            "maximum": 16
          }
        }
      },
      "CompleteResponse": {
        "type": "object",
        "required": [
          "text",
          "provider",
          "model",
          "usage",
          "failoverCount"
        ],
        "properties": {
          "text": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "usage": {
            "type": "object",
            "required": [
              "promptTokens",
              "completionTokens"
            ],
            "properties": {
              "promptTokens": {
                "type": "integer",
                "minimum": 0
              },
              "completionTokens": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "failoverCount": {
            "type": "integer",
            "minimum": 0
          },
          "scoresRunId": {
            "type": [
              "string",
              "null"
            ]
          },
          "toolCalls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatToolCall"
            }
          }
        }
      },
      "AgentIntent": {
        "type": "object",
        "description": "Routing defaults stored on an agent row. Hydrated into a session's `RoutingIntent` when the session is created with `agentId` and no inline `intent`. Note: agents accept a smaller `optimizeFor` enum than the per-session `RoutingIntent`.",
        "required": [
          "language"
        ],
        "properties": {
          "language": {
            "type": "string",
            "minLength": 2,
            "description": "BCP-47 language tag, e.g. `en`, `en-US`, `es-MX`.",
            "examples": [
              "en-US"
            ]
          },
          "optimizeFor": {
            "type": "string",
            "enum": [
              "latency",
              "quality",
              "cost"
            ]
          }
        }
      },
      "AgentLlmOptions": {
        "type": "object",
        "description": "Optional LLM tuning defaults stored on an agent. Strict object — only these three keys are accepted.",
        "additionalProperties": false,
        "properties": {
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2
          },
          "maxTokens": {
            "type": "integer",
            "minimum": 1
          },
          "model": {
            "type": "string",
            "description": "Pin a specific LLM model id. When omitted, the router picks per intent."
          }
        }
      },
      "Agent": {
        "type": "object",
        "description": "Reusable voice persona — `systemPrompt` + optional `voice` + routing `intent` defaults, plus optional `llmOptions`. Pass the `id` as `agentId` on `POST /v1/sessions` to seed a session from this agent.",
        "required": [
          "id",
          "organizationId",
          "name",
          "systemPrompt",
          "voice",
          "intent",
          "llmOptions",
          "createdAt",
          "updatedAt",
          "stackPreferences",
          "turnHandling"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Agent id, prefixed `agent_`.",
            "examples": [
              "agent_01HW..."
            ]
          },
          "organizationId": {
            "type": "string",
            "description": "Owning organization. Always equal to the authenticated org — agents are never visible across orgs."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Human-readable name. Unique within an organization."
          },
          "systemPrompt": {
            "type": "string",
            "minLength": 1,
            "description": "Initial agent instructions. Hydrated as the session's `systemPrompt` default when no per-call value is supplied."
          },
          "voice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Default TTS voice id. `null` when unset — the router picks a sane default per provider."
          },
          "intent": {
            "$ref": "#/components/schemas/AgentIntent"
          },
          "llmOptions": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentLlmOptions"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional LLM tuning defaults. `null` when unset."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "stackPreferences": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentStackPreferences"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-agent stack preferences. `null` when the agent has no preferences set."
          },
          "turnHandling": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentTurnHandling"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-agent turn-taking posture. `null` when the agent runs the platform defaults."
          },
          "maxDurationSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Hard cap on each of this agent's phone calls in seconds, used when the call names none. `null` = the 3600-second default."
          },
          "idleRePrompts": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "delayMs": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 600000
                  },
                  "maxPrompts": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10
                  },
                  "messages": {
                    "minItems": 1,
                    "maxItems": 10,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 500
                    }
                  },
                  "initialDelayMs": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 600000,
                    "description": "Silence, in milliseconds, before the FIRST check-in while the caller has not said anything yet: from call start, or from answer on an outbound call. Omit to use `delayMs`. Later check-ins, and any check-in after the caller has spoken, wait `delayMs`."
                  },
                  "hangupOnExhausted": {
                    "type": "boolean",
                    "default": true,
                    "description": "When the last check-in goes unanswered for one more `delayMs`, hang up (`true`, default). `false` keeps the line open in silence until the caller speaks or `maxDurationSeconds` ends it."
                  }
                },
                "required": [
                  "enabled"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Idle check-ins (\"are you still there?\"). `null` when none are configured."
          },
          "languageRouting": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "maxDetectionTurns": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 6
                  },
                  "languages": {
                    "minItems": 1,
                    "maxItems": 4,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "language": {
                          "type": "string",
                          "minLength": 2,
                          "maxLength": 35
                        },
                        "allowedProviders": {
                          "type": "object",
                          "properties": {
                            "stt": {
                              "maxItems": 8,
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              }
                            },
                            "llm": {
                              "maxItems": 8,
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              }
                            },
                            "tts": {
                              "maxItems": 8,
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              }
                            }
                          },
                          "additionalProperties": false
                        },
                        "voice": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 200
                        },
                        "systemPrompt": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 100000
                        },
                        "firstMessage": {
                          "type": "string",
                          "maxLength": 2000,
                          "description": "Opening line for calls in this language. `\"\"` = listen first. See \"Greeting precedence\" in the multilingual agents guide."
                        },
                        "inboundFirstMessage": {
                          "type": "string",
                          "maxLength": 2000,
                          "description": "Opening line for inbound calls in this language. Wins over this entry's `firstMessage`."
                        },
                        "outboundFirstMessage": {
                          "type": "string",
                          "maxLength": 2000,
                          "description": "Opening line for outbound calls in this language. Wins over this entry's `firstMessage`."
                        },
                        "voicemailMessage": {
                          "type": "string",
                          "maxLength": 2000,
                          "description": "Voicemail message for calls in this language (`onMachine: leave_message`)."
                        }
                      },
                      "required": [
                        "language"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "continuous": {
                    "type": "boolean",
                    "description": "Keep following the caller's language after it settles. Omit for the platform default."
                  },
                  "switchTurns": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 6,
                    "description": "Consecutive turns in another language before the call switches to it, after the detection window. Omit for 1."
                  }
                },
                "required": [
                  "enabled",
                  "languages"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-language routing and text table. `null` when the agent has none."
          }
        }
      },
      "CreateAgentRequest": {
        "type": "object",
        "required": [
          "name",
          "systemPrompt",
          "intent"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Human-readable display name. Does not need to be unique — agents are identified by `id`."
          },
          "systemPrompt": {
            "type": "string",
            "minLength": 1,
            "description": "Initial agent instructions used as the session-level `systemPrompt` default."
          },
          "voice": {
            "type": "string",
            "description": "Default TTS voice id. Omit to let the router pick per provider."
          },
          "intent": {
            "$ref": "#/components/schemas/AgentIntent"
          },
          "llmOptions": {
            "$ref": "#/components/schemas/AgentLlmOptions"
          },
          "stackPreferences": {
            "$ref": "#/components/schemas/AgentStackPreferences"
          },
          "turnHandling": {
            "$ref": "#/components/schemas/AgentTurnHandling"
          },
          "audioOutput": {
            "type": "object",
            "description": "Static gain in dB on the agent's own speech, applied before the audio reaches the call (and so before a phone leg), whichever TTS provider spoke. Negative is quieter: `-6` roughly halves the amplitude, for voices mastered hotter than telephone speech. Boost saturates at full scale, so it clips a voice that already peaks near 0 dBFS. `0` is a passthrough. Background audio is not affected.",
            "properties": {
              "gainDb": {
                "type": "number",
                "minimum": -24,
                "maximum": 12
              }
            },
            "required": [
              "gainDb"
            ],
            "additionalProperties": false
          },
          "backgroundAudio": {
            "type": "object",
            "properties": {
              "ambient": {
                "type": "object",
                "properties": {
                  "clip": {
                    "type": "string",
                    "enum": [
                      "office-ambience",
                      "city-ambience",
                      "forest-ambience",
                      "crowded-room",
                      "keyboard-typing",
                      "keyboard-typing2"
                    ]
                  },
                  "volume": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 16
                  }
                },
                "required": [
                  "clip"
                ],
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "endCall": {
            "type": "object",
            "description": "Deprecated. The `end_call` tool is always on for every agent. `{ \"enabled\": true }` is accepted and ignored; `{ \"enabled\": false }` is rejected with a 400 `VALIDATION_ERROR` instead of being silently dropped. Responses always report `{ \"enabled\": true }`.",
            "deprecated": true,
            "properties": {
              "enabled": {
                "type": "boolean",
                "enum": [
                  true
                ]
              }
            },
            "required": [
              "enabled"
            ],
            "additionalProperties": false
          },
          "firstMessage": {
            "type": "string",
            "maxLength": 2000,
            "description": "Opening line for any call without a direction-specific override (`inboundFirstMessage` / `outboundFirstMessage`). `\"\"` (empty string) means say nothing until the other side speaks; omitted means the default greeting for the call language (\"Hi, how can I help?\" in English)."
          },
          "idleRePrompts": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "delayMs": {
                "type": "integer",
                "minimum": 1000,
                "maximum": 600000
              },
              "maxPrompts": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10
              },
              "messages": {
                "minItems": 1,
                "maxItems": 10,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 500
                }
              },
              "initialDelayMs": {
                "type": "integer",
                "minimum": 1000,
                "maximum": 600000,
                "description": "Silence, in milliseconds, before the FIRST check-in while the caller has not said anything yet: from call start, or from answer on an outbound call. Omit to use `delayMs`. Later check-ins, and any check-in after the caller has spoken, wait `delayMs`."
              },
              "hangupOnExhausted": {
                "type": "boolean",
                "default": true,
                "description": "When the last check-in goes unanswered for one more `delayMs`, hang up (`true`, default). `false` keeps the line open in silence until the caller speaks or `maxDurationSeconds` ends it."
              }
            },
            "required": [
              "enabled"
            ],
            "additionalProperties": false
          },
          "inboundFirstMessage": {
            "type": "string",
            "maxLength": 2000,
            "description": "Opening line on inbound calls. Wins over `firstMessage`. `\"\"` (empty string) means say nothing until the caller speaks; omitted falls back to `firstMessage`, then to the default greeting for the call language."
          },
          "languageRouting": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "maxDetectionTurns": {
                "type": "integer",
                "minimum": 1,
                "maximum": 6
              },
              "languages": {
                "minItems": 1,
                "maxItems": 4,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "language": {
                      "type": "string",
                      "minLength": 2,
                      "maxLength": 35
                    },
                    "allowedProviders": {
                      "type": "object",
                      "properties": {
                        "stt": {
                          "maxItems": 8,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1
                          }
                        },
                        "llm": {
                          "maxItems": 8,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1
                          }
                        },
                        "tts": {
                          "maxItems": 8,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1
                          }
                        }
                      },
                      "additionalProperties": false
                    },
                    "voice": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200
                    },
                    "systemPrompt": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 100000
                    },
                    "firstMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Opening line for calls in this language. `\"\"` = listen first. See \"Greeting precedence\" in the multilingual agents guide."
                    },
                    "inboundFirstMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Opening line for inbound calls in this language. Wins over this entry's `firstMessage`."
                    },
                    "outboundFirstMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Opening line for outbound calls in this language. Wins over this entry's `firstMessage`."
                    },
                    "voicemailMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Voicemail message for calls in this language (`onMachine: leave_message`)."
                    }
                  },
                  "required": [
                    "language"
                  ],
                  "additionalProperties": false
                }
              },
              "continuous": {
                "type": "boolean",
                "description": "Keep following the caller's language after it settles. Omit for the platform default."
              },
              "switchTurns": {
                "type": "integer",
                "minimum": 1,
                "maximum": 6,
                "description": "Consecutive turns in another language before the call switches to it, after the detection window. Omit for 1."
              }
            },
            "required": [
              "enabled",
              "languages"
            ],
            "additionalProperties": false
          },
          "outboundFirstMessage": {
            "type": "string",
            "maxLength": 2000,
            "description": "Opening line on outbound calls. Wins over `firstMessage`. A non-empty string is spoken as soon as the call connects. `\"\"` (empty string) means say nothing: the agent listens until the callee speaks. Omitting it is NOT silence: the call falls back to `firstMessage`, and if that is also unset, to the default greeting for the call language (\"Hi, how can I help?\" in English). Set `\"\"` to open in silence."
          },
          "promptVariables": {
            "maxItems": 100,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 80,
                  "pattern": "^[\\w-]+(\\.[\\w-]+)*$"
                },
                "defaultValue": {
                  "type": "string",
                  "maxLength": 1000
                },
                "description": {
                  "type": "string",
                  "maxLength": 500
                }
              },
              "required": [
                "name"
              ],
              "additionalProperties": false
            }
          },
          "runMode": {
            "type": "string",
            "enum": [
              "cascade",
              "s2s"
            ]
          },
          "graphClassifier": {
            "type": "string",
            "enum": [
              "llm",
              "jev",
              "gpt-live"
            ],
            "default": "llm",
            "description": "Which judge answers `llm` edge conditions in graph mode. `llm` (the default) runs a temperature-0 one-shot on this agent's own cascade model. `jev` routes the decision through TypeSafe's System One as a choice over the candidate edges, returning a calibrated confidence per branch. Ignored by agents with no graph. `gpt-live` walks the graph on the agent's own GPT-Live speech-to-speech session; a call that runs on the cascade judges with `llm`. Any failure of the `jev` judge falls back to the `llm` path rather than failing the call."
          },
          "ringTimeoutSeconds": {
            "type": "integer",
            "minimum": 20,
            "maximum": 45,
            "description": "How long this agent's outbound calls ring before they end as a no-answer, in whole seconds. Counted from when the callee's phone starts ringing, or from dial if the carrier never reports ringing. Omit for the 45-second default. A per-call `ringTimeoutSeconds` on `POST /v1/sessions/phone` wins. A value outside 20–45 is rejected with 400 `RING_TIMEOUT_OUT_OF_RANGE`."
          },
          "outboundDtmf": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[0-9*#wW,]*[0-9*#][0-9*#wW,]*$",
            "description": "Keys this agent's outbound calls press after the far end answers, before the agent's first turn: a phone-menu path you already know, such as `8w2` (press 8, wait, press 2). Digits 0-9, `*` and `#`, with `w` or `,` for a half-second pause; at least one key. Pauses are stored as `w`. A per-call `telephony.dtmf` on `POST /v1/sessions/phone` wins, and `\"\"` there skips it for one call. Not pressed on scheduled callbacks or on calls started from an iMessage number. Takes effect at once and is not part of drafts or versions.",
            "example": "8w2"
          },
          "runtime": {
            "type": "string",
            "enum": [
              "livekit",
              "pipecat"
            ],
            "description": "Managed-agent runtime. It is chosen for the whole organization, not per agent: omit it, or send the organization's current runtime — any other value is refused with `409 RUNTIME_CONTROLLED_BY_ORGANIZATION`. The `pipecat` runtime runs `runMode: cascade` only."
          },
          "speechNormalization": {
            "type": "object",
            "properties": {
              "pronunciationDictionary": {
                "type": "object",
                "propertyNames": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "additionalProperties": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 240
                }
              },
              "textReplacements": {
                "type": "object",
                "propertyNames": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "additionalProperties": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 240
                }
              }
            },
            "additionalProperties": false
          },
          "sttMissRecovery": {
            "type": "object",
            "properties": {
              "message": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500
              }
            },
            "additionalProperties": false
          },
          "sttOptions": {
            "type": "object",
            "properties": {
              "keywords": {
                "maxItems": 200,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 80
                }
              },
              "prompt": {
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "language": {
                "type": "string",
                "pattern": "^[a-zA-Z][a-zA-Z0-9-]{1,15}$"
              },
              "workload": {
                "type": "string",
                "enum": [
                  "conversation",
                  "transcription",
                  "narration"
                ]
              },
              "diarization": {
                "type": "boolean"
              },
              "speakersExpected": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10
              },
              "smartFormat": {
                "type": "boolean"
              },
              "fillerWords": {
                "type": "boolean"
              },
              "profanityFilter": {
                "type": "boolean"
              },
              "providerOptions": {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {}
                }
              }
            },
            "additionalProperties": false
          },
          "ttsOptions": {
            "type": "object",
            "properties": {
              "speed": {
                "type": "number",
                "minimum": 0.5,
                "maximum": 2
              },
              "model": {
                "type": "string"
              },
              "workload": {
                "type": "string",
                "enum": [
                  "conversation",
                  "transcription",
                  "narration"
                ]
              },
              "providerOptions": {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {}
                }
              }
            },
            "additionalProperties": false
          },
          "voiceInstructions": {
            "type": "string",
            "maxLength": 2000
          },
          "webhooks": {
            "type": "object",
            "properties": {
              "preCall": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "postCall": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  },
                  "extractionFields": {
                    "maxItems": 30,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64,
                          "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "string",
                            "number",
                            "boolean",
                            "enum"
                          ]
                        },
                        "description": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 10000
                        },
                        "options": {
                          "minItems": 1,
                          "maxItems": 50,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 120
                          }
                        }
                      },
                      "required": [
                        "name",
                        "type",
                        "description"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "status": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "analysis": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "recording": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "timezone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "extractionFields": {
            "maxItems": 30,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 64,
                  "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "number",
                    "boolean",
                    "enum"
                  ]
                },
                "description": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 10000
                },
                "options": {
                  "minItems": 1,
                  "maxItems": 50,
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  }
                }
              },
              "required": [
                "name",
                "type",
                "description"
              ],
              "additionalProperties": false
            }
          },
          "maxDurationSeconds": {
            "type": "integer",
            "minimum": 30,
            "maximum": 14400,
            "description": "Hard cap on each of this agent's phone calls, in whole seconds (30-14400), used when the call names none. A per-call `maxDurationSeconds` on `POST /v1/sessions/phone` wins. Omit for the default (3600). A Realtime (`runMode: s2s`) call is still held to its one-hour ceiling. It takes effect on the agent at once and is not part of drafts: a draft save that includes it is rejected with 400 `MAX_DURATION_NOT_DRAFTABLE`."
          }
        }
      },
      "UpdateAgentRequest": {
        "type": "object",
        "description": "Partial update — every field is optional. Only supplied keys are written.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Human-readable display name. Does not need to be unique — agents are identified by `id`."
          },
          "systemPrompt": {
            "type": "string",
            "minLength": 1
          },
          "voice": {
            "type": "string"
          },
          "intent": {
            "$ref": "#/components/schemas/AgentIntent"
          },
          "llmOptions": {
            "$ref": "#/components/schemas/AgentLlmOptions"
          },
          "stackPreferences": {
            "$ref": "#/components/schemas/AgentStackPreferences"
          },
          "turnHandling": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentTurnHandling"
              },
              {
                "type": "null"
              }
            ],
            "description": "Replaces the stored posture wholesale — supplied keys are not merged into the existing object. `null` clears it back to the platform defaults."
          },
          "audioOutput": {
            "type": "object",
            "description": "Static gain in dB on the agent's own speech, applied before the audio reaches the call (and so before a phone leg), whichever TTS provider spoke. Negative is quieter: `-6` roughly halves the amplitude, for voices mastered hotter than telephone speech. Boost saturates at full scale, so it clips a voice that already peaks near 0 dBFS. `0` is a passthrough. Background audio is not affected. Send `null` to remove the gain.",
            "properties": {
              "gainDb": {
                "type": "number",
                "minimum": -24,
                "maximum": 12
              }
            },
            "required": [
              "gainDb"
            ],
            "additionalProperties": false
          },
          "backgroundAudio": {
            "type": "object",
            "properties": {
              "ambient": {
                "type": "object",
                "properties": {
                  "clip": {
                    "type": "string",
                    "enum": [
                      "office-ambience",
                      "city-ambience",
                      "forest-ambience",
                      "crowded-room",
                      "keyboard-typing",
                      "keyboard-typing2"
                    ]
                  },
                  "volume": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 16
                  }
                },
                "required": [
                  "clip"
                ],
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "endCall": {
            "type": "object",
            "description": "Deprecated. The `end_call` tool is always on for every agent. `{ \"enabled\": true }` is accepted and ignored; `{ \"enabled\": false }` is rejected with a 400 `VALIDATION_ERROR` instead of being silently dropped. Responses always report `{ \"enabled\": true }`.",
            "deprecated": true,
            "properties": {
              "enabled": {
                "type": "boolean",
                "enum": [
                  true
                ]
              }
            },
            "required": [
              "enabled"
            ],
            "additionalProperties": false
          },
          "firstMessage": {
            "type": "string",
            "maxLength": 2000,
            "nullable": true,
            "description": "Opening line for any call without a direction-specific override (`inboundFirstMessage` / `outboundFirstMessage`). `\"\"` (empty string) means say nothing until the other side speaks; `null` clears a stored value; once cleared (or never set) the call uses the default greeting for the call language (\"Hi, how can I help?\" in English)."
          },
          "idleRePrompts": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "delayMs": {
                "type": "integer",
                "minimum": 1000,
                "maximum": 600000
              },
              "maxPrompts": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10
              },
              "messages": {
                "minItems": 1,
                "maxItems": 10,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 500
                }
              },
              "initialDelayMs": {
                "type": "integer",
                "minimum": 1000,
                "maximum": 600000,
                "description": "Silence, in milliseconds, before the FIRST check-in while the caller has not said anything yet: from call start, or from answer on an outbound call. Omit to use `delayMs`. Later check-ins, and any check-in after the caller has spoken, wait `delayMs`."
              },
              "hangupOnExhausted": {
                "type": "boolean",
                "default": true,
                "description": "When the last check-in goes unanswered for one more `delayMs`, hang up (`true`, default). `false` keeps the line open in silence until the caller speaks or `maxDurationSeconds` ends it."
              }
            },
            "required": [
              "enabled"
            ],
            "additionalProperties": false
          },
          "inboundFirstMessage": {
            "type": "string",
            "maxLength": 2000,
            "nullable": true,
            "description": "Opening line on inbound calls. Wins over `firstMessage`. `\"\"` (empty string) means say nothing until the caller speaks; `null` clears a stored value; once cleared (or never set) the call falls back to `firstMessage`, then to the default greeting for the call language."
          },
          "languageRouting": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "maxDetectionTurns": {
                "type": "integer",
                "minimum": 1,
                "maximum": 6
              },
              "languages": {
                "minItems": 1,
                "maxItems": 4,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "language": {
                      "type": "string",
                      "minLength": 2,
                      "maxLength": 35
                    },
                    "allowedProviders": {
                      "type": "object",
                      "properties": {
                        "stt": {
                          "maxItems": 8,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1
                          }
                        },
                        "llm": {
                          "maxItems": 8,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1
                          }
                        },
                        "tts": {
                          "maxItems": 8,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1
                          }
                        }
                      },
                      "additionalProperties": false
                    },
                    "voice": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200
                    },
                    "systemPrompt": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 100000
                    },
                    "firstMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Opening line for calls in this language. `\"\"` = listen first. See \"Greeting precedence\" in the multilingual agents guide."
                    },
                    "inboundFirstMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Opening line for inbound calls in this language. Wins over this entry's `firstMessage`."
                    },
                    "outboundFirstMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Opening line for outbound calls in this language. Wins over this entry's `firstMessage`."
                    },
                    "voicemailMessage": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Voicemail message for calls in this language (`onMachine: leave_message`)."
                    }
                  },
                  "required": [
                    "language"
                  ],
                  "additionalProperties": false
                }
              },
              "continuous": {
                "type": "boolean",
                "description": "Keep following the caller's language after it settles. Omit for the platform default."
              },
              "switchTurns": {
                "type": "integer",
                "minimum": 1,
                "maximum": 6,
                "description": "Consecutive turns in another language before the call switches to it, after the detection window. Omit for 1."
              }
            },
            "required": [
              "enabled",
              "languages"
            ],
            "additionalProperties": false
          },
          "outboundFirstMessage": {
            "type": "string",
            "maxLength": 2000,
            "nullable": true,
            "description": "Opening line on outbound calls. Wins over `firstMessage`. A non-empty string is spoken as soon as the call connects. `\"\"` (empty string) means say nothing: the agent listens until the callee speaks. `null` clears a stored value, and a cleared (or never set) value is NOT silence: the call falls back to `firstMessage`, and if that is also unset, to the default greeting for the call language (\"Hi, how can I help?\" in English). Set `\"\"` to open in silence."
          },
          "promptVariables": {
            "maxItems": 100,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 80,
                  "pattern": "^[\\w-]+(\\.[\\w-]+)*$"
                },
                "defaultValue": {
                  "type": "string",
                  "maxLength": 1000
                },
                "description": {
                  "type": "string",
                  "maxLength": 500
                }
              },
              "required": [
                "name"
              ],
              "additionalProperties": false
            }
          },
          "runMode": {
            "type": "string",
            "enum": [
              "cascade",
              "s2s"
            ]
          },
          "graphClassifier": {
            "type": "string",
            "enum": [
              "llm",
              "jev",
              "gpt-live"
            ],
            "default": "llm",
            "description": "Which judge answers `llm` edge conditions in graph mode. `llm` (the default) runs a temperature-0 one-shot on this agent's own cascade model. `jev` routes the decision through TypeSafe's System One as a choice over the candidate edges, returning a calibrated confidence per branch. Ignored by agents with no graph. `gpt-live` walks the graph on the agent's own GPT-Live speech-to-speech session; a call that runs on the cascade judges with `llm`. Any failure of the `jev` judge falls back to the `llm` path rather than failing the call."
          },
          "ringTimeoutSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 20,
            "maximum": 45,
            "description": "How long this agent's outbound calls ring before they end as a no-answer, in whole seconds (20–45). `null` restores the 45-second default. A per-call `ringTimeoutSeconds` on `POST /v1/sessions/phone` wins. A value outside 20–45 is rejected with 400 `RING_TIMEOUT_OUT_OF_RANGE`."
          },
          "outboundDtmf": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[0-9*#wW,]*[0-9*#][0-9*#wW,]*$",
            "description": "Keys this agent's outbound calls press after answer, before the agent's first turn (see `outboundDtmf` on create). `null` stops pressing keys."
          },
          "runtime": {
            "type": "string",
            "enum": [
              "livekit",
              "pipecat"
            ],
            "description": "Managed-agent runtime. Omit it: the runtime that actually serves the agent is chosen for the whole organization, and agent responses carry that effective value, never the value sent here. A value sent here is stored on the agent without changing the organization's runtime, and while the stored value is `pipecat`, `runMode: s2s` is refused with `400 INCOMPATIBLE_RUNTIME_MODE` (the `pipecat` runtime runs `runMode: cascade` only)."
          },
          "speechNormalization": {
            "type": "object",
            "properties": {
              "pronunciationDictionary": {
                "type": "object",
                "propertyNames": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "additionalProperties": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 240
                }
              },
              "textReplacements": {
                "type": "object",
                "propertyNames": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "additionalProperties": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 240
                }
              }
            },
            "additionalProperties": false
          },
          "sttMissRecovery": {
            "type": "object",
            "properties": {
              "message": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500
              }
            },
            "additionalProperties": false
          },
          "sttOptions": {
            "type": "object",
            "properties": {
              "keywords": {
                "maxItems": 200,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 80
                }
              },
              "prompt": {
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "language": {
                "type": "string",
                "pattern": "^[a-zA-Z][a-zA-Z0-9-]{1,15}$"
              },
              "workload": {
                "type": "string",
                "enum": [
                  "conversation",
                  "transcription",
                  "narration"
                ]
              },
              "diarization": {
                "type": "boolean"
              },
              "speakersExpected": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10
              },
              "smartFormat": {
                "type": "boolean"
              },
              "fillerWords": {
                "type": "boolean"
              },
              "profanityFilter": {
                "type": "boolean"
              },
              "providerOptions": {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {}
                }
              }
            },
            "additionalProperties": false
          },
          "timezone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "ttsOptions": {
            "type": "object",
            "properties": {
              "speed": {
                "type": "number",
                "minimum": 0.5,
                "maximum": 2
              },
              "model": {
                "type": "string"
              },
              "workload": {
                "type": "string",
                "enum": [
                  "conversation",
                  "transcription",
                  "narration"
                ]
              },
              "providerOptions": {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {}
                }
              }
            },
            "additionalProperties": false
          },
          "voiceInstructions": {
            "type": "string",
            "maxLength": 2000
          },
          "webhooks": {
            "type": "object",
            "properties": {
              "preCall": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "postCall": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  },
                  "extractionFields": {
                    "maxItems": 30,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64,
                          "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "string",
                            "number",
                            "boolean",
                            "enum"
                          ]
                        },
                        "description": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 10000
                        },
                        "options": {
                          "minItems": 1,
                          "maxItems": 50,
                          "type": "array",
                          "items": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 120
                          }
                        }
                      },
                      "required": [
                        "name",
                        "type",
                        "description"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "status": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "analysis": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              },
              "recording": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "secret": {
                    "type": "string",
                    "minLength": 8
                  },
                  "headers": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "authHeaders": {
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z0-9!#$%&'*+.^_`|~-]+$"
                        },
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8192
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 8000
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "extractionFields": {
            "maxItems": 30,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 64,
                  "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "number",
                    "boolean",
                    "enum"
                  ]
                },
                "description": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 10000
                },
                "options": {
                  "minItems": 1,
                  "maxItems": 50,
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  }
                }
              },
              "required": [
                "name",
                "type",
                "description"
              ],
              "additionalProperties": false
            }
          },
          "maxDurationSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 30,
            "maximum": 14400,
            "description": "Hard cap on each of this agent's phone calls, in whole seconds (30-14400), used when the call names none. A per-call `maxDurationSeconds` on `POST /v1/sessions/phone` wins. Omit for the default (3600). A Realtime (`runMode: s2s`) call is still held to its one-hour ceiling. It takes effect on the agent at once and is not part of drafts: a draft save that includes it is rejected with 400 `MAX_DURATION_NOT_DRAFTABLE`. `null` restores the default."
          }
        }
      },
      "AgentTurnHandling": {
        "type": "object",
        "description": "Per-agent turn-taking posture, stored on the agent and forwarded to every call it runs. A per-call `turnHandling` on `POST /v1/sessions` or `POST /v1/sessions/phone` REPLACES this object wholesale — the two are not merged per field.",
        "properties": {
          "profile": {
            "type": "string",
            "enum": [
              "conversational",
              "ivr"
            ],
            "default": "conversational",
            "description": "Turn-taking posture preset. `ivr` is for calls INTO phone menus: it unlocks the `send_dtmf` keypad tool plus the menu-loop and dead-air watch — armed by the answering-machine verdict on outbound calls, and from call start elsewhere. Turn-taking itself stays conversational under both presets. The legacy value `ivr_patient` is still accepted and normalized to `ivr`."
          },
          "endpointing": {
            "type": "object",
            "description": "How long a caller has to stay silent before their turn is committed.",
            "properties": {
              "minDelay": {
                "type": "integer",
                "minimum": 0,
                "maximum": 5000,
                "description": "Milliseconds."
              },
              "maxDelay": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10000,
                "description": "Milliseconds."
              }
            }
          },
          "interruption": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "adaptive",
                  "vad"
                ],
                "description": "`vad` barges in on any overlapping speech, honoring `minDuration`/`minWords`. `adaptive` classifies a real interruption from a backchannel (\"mhm\", \"right\") first and ignores both thresholds. Unset uses the platform default."
              },
              "minDuration": {
                "type": "integer",
                "minimum": 0,
                "maximum": 5000,
                "description": "Milliseconds of caller speech required to interrupt. `vad` mode only."
              },
              "minWords": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10,
                "description": "Words required to interrupt. `vad` mode only."
              }
            }
          },
          "turnDetection": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "stt"
                ]
              }
            ],
            "description": "End-of-turn detection. Unset uses the semantic turn model; `false` falls back to plain voice-activity detection; `\"stt\"` commits on final transcripts without waiting for acoustic end-of-speech (experimental)."
          },
          "contextThreshold": {
            "type": "boolean",
            "description": "Context-conditioned turn-taking: the commit threshold adapts to the shape of what the agent just asked. Unset uses the platform default."
          },
          "heardBy": {
            "type": "string",
            "enum": [
              "content",
              "timing"
            ],
            "description": "Graph agents: how a caller turn is judged to have heard the scripted step's question. `content` checks that the question had fully played (LiveKit played text) before the caller's first transcribed word, and re-asks only the question otherwise; `timing` (default) uses the original audio-timing rules."
          },
          "dtmfToolDescription": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Replaces the built-in description the model sees on the `send_dtmf` keypad tool — for steering when and what the agent presses on a specific menu. Only meaningful with the `ivr` profile, which is what creates the tool."
          },
          "amdPrompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Replaces the built-in prompt the answering-machine classifier sees when deciding whether a person, a phone menu, or voicemail answered. Write it as classification instructions with examples; the callee's greeting transcript is appended as the input. The category vocabulary is always supplied for you, so a prompt that never names the categories still works. Outbound calls only — inbound never runs the detector, and this is accepted and inert there. It steers only the judgement on what was said: a pickup with no speech at all is decided on timing before the prompt is read. On a Realtime (`runMode: s2s`) call it steers the GPT-Live detector, which reads the voice model's own transcript of the greeting; Gemini Live runs no detector."
          },
          "waitForCallee": {
            "type": "boolean",
            "default": true,
            "description": "Outbound opening etiquette: after pickup, hold the opening line briefly so the callee's own hello (\"Good morning, Riverside Dental…\") finishes first. `false` greets the instant they answer."
          },
          "recordCallerKeypad": {
            "type": "boolean",
            "default": false,
            "description": "Record the keys the caller presses, in the session's `dtmf` facts and the `call.report` webhook. Off unless `true`, because callers type PINs and card numbers: while off, those keys are never stored or sent, and `dtmf` lists only the keys the agent pressed with `send_dtmf`, which are always recorded."
          },
          "onMachine": {
            "type": "string",
            "enum": [
              "hangup",
              "leave_message",
              "agent_decides",
              "continue"
            ],
            "default": "agent_decides",
            "description": "What happens when answering-machine detection says a recordable VOICEMAIL greeting answered an outbound call. `hangup` ends the call the moment the verdict lands. `leave_message` waits for the greeting to finish, speaks `voicemailMessage` once in the agent's voice, then hangs up. `agent_decides` (default) hands the verdict to the model as a system note and lets the prompt's own voicemail rules act. `continue` ignores the verdict and carries on as a normal call: no system note, no hangup and no `machineBudgetSeconds` limit. The verdict is recorded as a `worker.amd_action_gated` event with reason `on_machine_continue`, and the call is not labelled a voicemail. On a cascade call, a call-screening phase that reached the mailbox keeps its own limit. An unavailable mailbox (full or not set up) always ends the call without a message, whatever this says. Never applies to phone menus or call-screening services, which lead to a person. Calls ended by `hangup` or `leave_message` close with `endReason: voicemail_reached` and report outcome `voicemail`. On a Realtime (`runMode: s2s`) call this applies only to GPT-Live, and only once worker detection is enabled for the workspace (until then it just records verdicts), with `leave_message` spoken in the voice model's own words (Gemini Live runs no worker detection)."
          },
          "onHuman": {
            "type": "string",
            "enum": [
              "hangup",
              "continue"
            ],
            "default": "continue",
            "description": "What happens when a live PERSON answers an outbound call. `hangup` is for campaigns that should only ever reach a mailbox: the agent is muted from pickup and no opening line is sent; when a person answers (they speak, or stay silent for about 4 seconds) Speko hangs up without the agent saying anything, and the session closes with `endReason: human_answered`. When a machine answers instead (voicemail, a phone menu, a hold queue or a call screener), the agent is unmuted and the call continues under `onMachine`; a person who then picks up from a call screener is hung up on too. `continue` (default) is a normal call. Applies to Realtime (`runMode: s2s`) phone calls only; a cascade call ignores it."
          },
          "voicemailMessage": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Spoken once into the mailbox under `onMachine: leave_message`, after the greeting ends. Plain text, rendered by the agent's TTS voice. On a Realtime (GPT-Live) call there is no TTS: the voice model says it in its own words, after it is shortened to fit the model's instruction limit — about 1,200 characters of English, fewer in Chinese, Japanese, Korean or Thai — at a sentence boundary where one exists."
          },
          "holdTimeoutSeconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 7200,
            "description": "An explicit cap, in seconds, on how long the call waits on hold: the call ends this long after anyone on the line last said something conversational, even while hold music plays. Menu prompts, queue announcements (\"your call is important to us\") and hold music do not reset it; a person speaking does. When unset, the call waits on hold for as long as the line is alive (speech, announcements or hold music), up to `maxDurationSeconds`, and ends only after 600 seconds with no speech and no sound at all. Set it to give up on a queue sooner. On cascade calls it applies once a phone menu is detected. On Realtime (`runMode: s2s`) phone calls a value of 600 or more applies to the whole call, and a smaller one applies once a phone menu is detected (or from the start with `profile: 'ivr'`), so a quiet caller on a regular call is not cut off early. It never extends a call past `maxDurationSeconds`, which stays the hard cap. A call it ends reports `endReason: hold_timeout`."
          },
          "keypadAllowedKeys": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "0",
                "1",
                "2",
                "3",
                "4",
                "5",
                "6",
                "7",
                "8",
                "9",
                "*",
                "#"
              ]
            },
            "minItems": 1,
            "maxItems": 12,
            "uniqueItems": true,
            "description": "The only keys the agent's keypad tool (`send_dtmf`) may press, from `0`-`9`, `*` and `#`. The tool's description lists them to the model, and a press that names any other key is refused (nothing is sent) and the model is told which keys are allowed. Omit to allow every key. Does not affect `outboundDtmf` or `telephony.dtmf`, which press exactly what you saved."
          },
          "machineBudgetSeconds": {
            "type": "integer",
            "minimum": 30,
            "maximum": 600,
            "default": 120,
            "description": "How long, in seconds, the call may stay on the line after it reaches a recordable voicemail greeting, or a phone's call-screening service that never puts a person through, before it ends with `endReason: dead_air`. Counted from the verdict. Defaults to 120. It does not cut a `leave_message` voicemail off mid-sentence: once the message starts, its own playout bounds it. Phone menus and hold queues are not bounded by it; see `holdTimeoutSeconds`. A voicemail ignored under `onMachine: continue` is not bounded by it. On a Realtime (GPT-Live) call a detected voicemail that runs it out ends as `voicemail_reached`."
          },
          "vad": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "silero",
                  "ai-coustics"
                ]
              }
            },
            "required": [
              "provider"
            ],
            "additionalProperties": false,
            "description": "Local voice-activity detector. Omit for Silero."
          },
          "noiseCancellation": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "model": {
                "type": "string",
                "enum": [
                  "quail",
                  "quail-voice-focus"
                ]
              }
            },
            "required": [
              "enabled"
            ],
            "additionalProperties": false,
            "description": "Caller-leg noise cancellation. Omit for the platform default. `enabled: false` turns it off. `model` picks plain noise reduction (`quail`, default) or primary-speaker isolation (`quail-voice-focus`), and is only valid while `enabled` is true."
          },
          "keypad": {
            "type": "boolean",
            "description": "Opt out of the keypad tool. Unset builds `send_dtmf` for every phone agent and arms it when the call meets an automated menu (or from the first turn under `profile: 'ivr'`); `false` never builds it."
          },
          "textGate": {
            "type": "boolean",
            "description": "Let the transcript veto an end of turn the audio model called complete. Omit for the platform default."
          },
          "turnDetector": {
            "type": "string",
            "enum": [
              "smart_turn",
              "speko_turn_v1"
            ],
            "description": "Pins the end-of-turn detector for this agent's calls. Omit for the platform default."
          }
        }
      },
      "SessionTurnHandling": {
        "type": "object",
        "description": "Per-call turn-taking posture for a browser session. Replaces the agent's stored `turnHandling` wholesale for this session.",
        "properties": {
          "profile": {
            "type": "string",
            "enum": [
              "conversational",
              "ivr"
            ],
            "default": "conversational",
            "description": "Turn-taking posture preset. `ivr` is for calls INTO phone menus: it unlocks the `send_dtmf` keypad tool plus the menu-loop and dead-air watch — armed by the answering-machine verdict on outbound calls, and from call start elsewhere. Turn-taking itself stays conversational under both presets. The legacy value `ivr_patient` is still accepted and normalized to `ivr`."
          },
          "endpointing": {
            "type": "object",
            "description": "How long a caller has to stay silent before their turn is committed.",
            "properties": {
              "minDelay": {
                "type": "integer",
                "minimum": 0,
                "maximum": 5000,
                "description": "Milliseconds."
              },
              "maxDelay": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10000,
                "description": "Milliseconds."
              }
            }
          },
          "interruption": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "adaptive",
                  "vad"
                ],
                "description": "`vad` barges in on any overlapping speech, honoring `minDuration`/`minWords`. `adaptive` classifies a real interruption from a backchannel (\"mhm\", \"right\") first and ignores both thresholds. Unset uses the platform default."
              },
              "minDuration": {
                "type": "integer",
                "minimum": 0,
                "maximum": 5000,
                "description": "Milliseconds of caller speech required to interrupt. `vad` mode only."
              },
              "minWords": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10,
                "description": "Words required to interrupt. `vad` mode only."
              }
            }
          },
          "turnDetection": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "stt"
                ]
              }
            ],
            "description": "End-of-turn detection. Unset uses the semantic turn model; `false` falls back to plain voice-activity detection; `\"stt\"` commits on final transcripts without waiting for acoustic end-of-speech (experimental)."
          },
          "contextThreshold": {
            "type": "boolean",
            "description": "Context-conditioned turn-taking: the commit threshold adapts to the shape of what the agent just asked. Unset uses the platform default."
          },
          "heardBy": {
            "type": "string",
            "enum": [
              "content",
              "timing"
            ],
            "description": "Graph agents: how a caller turn is judged to have heard the scripted step's question. `content` checks that the question had fully played (LiveKit played text) before the caller's first transcribed word, and re-asks only the question otherwise; `timing` (default) uses the original audio-timing rules."
          },
          "dtmfToolDescription": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Replaces the built-in description the model sees on the `send_dtmf` keypad tool — for steering when and what the agent presses on a specific menu. Only meaningful with the `ivr` profile, which is what creates the tool."
          },
          "amdPrompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Replaces the built-in prompt the answering-machine classifier sees when deciding whether a person, a phone menu, or voicemail answered. Write it as classification instructions with examples; the callee's greeting transcript is appended as the input. The category vocabulary is always supplied for you, so a prompt that never names the categories still works. Outbound calls only — inbound never runs the detector, and this is accepted and inert there. It steers only the judgement on what was said: a pickup with no speech at all is decided on timing before the prompt is read. On a Realtime (`runMode: s2s`) call it steers the GPT-Live detector, which reads the voice model's own transcript of the greeting; Gemini Live runs no detector."
          },
          "recordCallerKeypad": {
            "type": "boolean",
            "default": false,
            "description": "Accepted so an agent's stored `turnHandling` can be sent back unchanged. A browser session has no phone keypad, so it has no effect here."
          },
          "onMachine": {
            "type": "string",
            "enum": [
              "hangup",
              "leave_message",
              "agent_decides",
              "continue"
            ],
            "default": "agent_decides",
            "description": "Per-call override of the agent's `onMachine`. What happens when answering-machine detection says a recordable VOICEMAIL greeting answered an outbound call. `hangup` ends the call the moment the verdict lands. `leave_message` waits for the greeting to finish, speaks `voicemailMessage` once in the agent's voice, then hangs up. `agent_decides` (default) hands the verdict to the model as a system note and lets the prompt's own voicemail rules act. `continue` ignores the verdict and carries on as a normal call: no system note, no hangup and no `machineBudgetSeconds` limit. The verdict is recorded as a `worker.amd_action_gated` event with reason `on_machine_continue`, and the call is not labelled a voicemail. On a cascade call, a call-screening phase that reached the mailbox keeps its own limit. An unavailable mailbox (full or not set up) always ends the call without a message, whatever this says. Never applies to phone menus or call-screening services, which lead to a person. Calls ended by `hangup` or `leave_message` close with `endReason: voicemail_reached` and report outcome `voicemail`. On a Realtime (`runMode: s2s`) call this applies only to GPT-Live, and only once worker detection is enabled for the workspace (until then it just records verdicts), with `leave_message` spoken in the voice model's own words (Gemini Live runs no worker detection)."
          },
          "onHuman": {
            "type": "string",
            "enum": [
              "hangup",
              "continue"
            ],
            "default": "continue",
            "description": "What happens when a live PERSON answers an outbound call. `hangup` is for campaigns that should only ever reach a mailbox: the agent is muted from pickup and no opening line is sent; when a person answers (they speak, or stay silent for about 4 seconds) Speko hangs up without the agent saying anything, and the session closes with `endReason: human_answered`. When a machine answers instead (voicemail, a phone menu, a hold queue or a call screener), the agent is unmuted and the call continues under `onMachine`; a person who then picks up from a call screener is hung up on too. `continue` (default) is a normal call. Applies to Realtime (`runMode: s2s`) phone calls only; a cascade call ignores it."
          },
          "voicemailMessage": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Spoken once into the mailbox under `onMachine: leave_message`, after the greeting ends. Plain text, rendered by the agent's TTS voice. On a Realtime (GPT-Live) call there is no TTS: the voice model says it in its own words, after it is shortened to fit the model's instruction limit — about 1,200 characters of English, fewer in Chinese, Japanese, Korean or Thai — at a sentence boundary where one exists."
          },
          "holdTimeoutSeconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 7200,
            "description": "An explicit cap, in seconds, on how long the call waits on hold: the call ends this long after anyone on the line last said something conversational, even while hold music plays. Menu prompts, queue announcements (\"your call is important to us\") and hold music do not reset it; a person speaking does. When unset, the call waits on hold for as long as the line is alive (speech, announcements or hold music), up to `maxDurationSeconds`, and ends only after 600 seconds with no speech and no sound at all. Set it to give up on a queue sooner. On cascade calls it applies once a phone menu is detected. On Realtime (`runMode: s2s`) phone calls a value of 600 or more applies to the whole call, and a smaller one applies once a phone menu is detected (or from the start with `profile: 'ivr'`), so a quiet caller on a regular call is not cut off early. It never extends a call past `maxDurationSeconds`, which stays the hard cap. A call it ends reports `endReason: hold_timeout`."
          },
          "keypadAllowedKeys": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "0",
                "1",
                "2",
                "3",
                "4",
                "5",
                "6",
                "7",
                "8",
                "9",
                "*",
                "#"
              ]
            },
            "minItems": 1,
            "maxItems": 12,
            "uniqueItems": true,
            "description": "Per-call override of the agent's value. The only keys the agent's keypad tool (`send_dtmf`) may press, from `0`-`9`, `*` and `#`. The tool's description lists them to the model, and a press that names any other key is refused (nothing is sent) and the model is told which keys are allowed. Omit to allow every key. Does not affect `outboundDtmf` or `telephony.dtmf`, which press exactly what you saved."
          },
          "machineBudgetSeconds": {
            "type": "integer",
            "minimum": 30,
            "maximum": 600,
            "default": 120,
            "description": "Per-call override of the agent's value. How long, in seconds, the call may stay on the line after it reaches a recordable voicemail greeting, or a phone's call-screening service that never puts a person through, before it ends with `endReason: dead_air`. Counted from the verdict. Defaults to 120. It does not cut a `leave_message` voicemail off mid-sentence: once the message starts, its own playout bounds it. Phone menus and hold queues are not bounded by it; see `holdTimeoutSeconds`. A voicemail ignored under `onMachine: continue` is not bounded by it. On a Realtime (GPT-Live) call a detected voicemail that runs it out ends as `voicemail_reached`."
          }
        }
      },
      "PhoneTurnHandling": {
        "type": "object",
        "description": "Per-call turn-taking posture for an outbound phone call — e.g. dialling a number known to answer with a menu. Replaces the agent's stored `turnHandling` wholesale for this call.",
        "properties": {
          "profile": {
            "type": "string",
            "enum": [
              "conversational",
              "ivr"
            ],
            "default": "conversational",
            "description": "Turn-taking posture preset. `ivr` is for calls INTO phone menus: it unlocks the `send_dtmf` keypad tool plus the menu-loop and dead-air watch — armed by the answering-machine verdict on outbound calls, and from call start elsewhere. Turn-taking itself stays conversational under both presets. The legacy value `ivr_patient` is still accepted and normalized to `ivr`."
          },
          "endpointing": {
            "type": "object",
            "description": "How long a caller has to stay silent before their turn is committed.",
            "properties": {
              "minDelay": {
                "type": "integer",
                "minimum": 0,
                "maximum": 5000,
                "description": "Milliseconds."
              },
              "maxDelay": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10000,
                "description": "Milliseconds."
              }
            }
          },
          "interruption": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "adaptive",
                  "vad"
                ],
                "description": "`vad` barges in on any overlapping speech, honoring `minDuration`/`minWords`. `adaptive` classifies a real interruption from a backchannel (\"mhm\", \"right\") first and ignores both thresholds. Unset uses the platform default."
              },
              "minDuration": {
                "type": "integer",
                "minimum": 0,
                "maximum": 5000,
                "description": "Milliseconds of caller speech required to interrupt. `vad` mode only."
              },
              "minWords": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10,
                "description": "Words required to interrupt. `vad` mode only."
              }
            }
          },
          "turnDetection": {
            "type": "boolean",
            "description": "End-of-turn detection. Unset uses the semantic turn model; `false` falls back to plain voice-activity detection."
          },
          "contextThreshold": {
            "type": "boolean",
            "description": "Context-conditioned turn-taking: the commit threshold adapts to the shape of what the agent just asked. Unset uses the platform default."
          },
          "heardBy": {
            "type": "string",
            "enum": [
              "content",
              "timing"
            ],
            "description": "Graph agents: how a caller turn is judged to have heard the scripted step's question. `content` checks that the question had fully played (LiveKit played text) before the caller's first transcribed word, and re-asks only the question otherwise; `timing` (default) uses the original audio-timing rules."
          },
          "amdPrompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Replaces the built-in prompt the answering-machine classifier sees when deciding whether a person, a phone menu, or voicemail answered. Write it as classification instructions with examples; the callee's greeting transcript is appended as the input. The category vocabulary is always supplied for you, so a prompt that never names the categories still works. Outbound calls only — inbound never runs the detector, and this is accepted and inert there. It steers only the judgement on what was said: a pickup with no speech at all is decided on timing before the prompt is read. On a Realtime (`runMode: s2s`) call it steers the GPT-Live detector, which reads the voice model's own transcript of the greeting; Gemini Live runs no detector."
          },
          "greetFirst": {
            "type": "boolean",
            "default": true,
            "description": "Play the opening line immediately on answer while answering-machine detection classifies in the background. `false` holds the OPENING LINE ONLY — never later replies — until the verdict lands, bounded at roughly 8 seconds, so the agent never greets a machine. Not honoured on a Realtime (`runMode: s2s`) call, where the opening is never held."
          },
          "waitForCallee": {
            "type": "boolean",
            "default": true,
            "description": "Outbound opening etiquette: after pickup, hold the opening line briefly so the callee's own hello (\"Good morning, Riverside Dental…\") finishes first. `false` greets the instant they answer."
          },
          "recordCallerKeypad": {
            "type": "boolean",
            "default": false,
            "description": "Record the keys the callee presses on this call. Overrides the agent's `turnHandling.recordCallerKeypad`. Off unless `true`: while off, `dtmf` lists only the keys the agent pressed with `send_dtmf`."
          },
          "onMachine": {
            "type": "string",
            "enum": [
              "hangup",
              "leave_message",
              "agent_decides",
              "continue"
            ],
            "default": "agent_decides",
            "description": "Per-call override of the agent's `onMachine`. What happens when answering-machine detection says a recordable VOICEMAIL greeting answered an outbound call. `hangup` ends the call the moment the verdict lands. `leave_message` waits for the greeting to finish, speaks `voicemailMessage` once in the agent's voice, then hangs up. `agent_decides` (default) hands the verdict to the model as a system note and lets the prompt's own voicemail rules act. `continue` ignores the verdict and carries on as a normal call: no system note, no hangup and no `machineBudgetSeconds` limit. The verdict is recorded as a `worker.amd_action_gated` event with reason `on_machine_continue`, and the call is not labelled a voicemail. On a cascade call, a call-screening phase that reached the mailbox keeps its own limit. An unavailable mailbox (full or not set up) always ends the call without a message, whatever this says. Never applies to phone menus or call-screening services, which lead to a person. Calls ended by `hangup` or `leave_message` close with `endReason: voicemail_reached` and report outcome `voicemail`. On a Realtime (`runMode: s2s`) call this applies only to GPT-Live, and only once worker detection is enabled for the workspace (until then it just records verdicts), with `leave_message` spoken in the voice model's own words (Gemini Live runs no worker detection)."
          },
          "onHuman": {
            "type": "string",
            "enum": [
              "hangup",
              "continue"
            ],
            "default": "continue",
            "description": "What happens when a live PERSON answers an outbound call. `hangup` is for campaigns that should only ever reach a mailbox: the agent is muted from pickup and no opening line is sent; when a person answers (they speak, or stay silent for about 4 seconds) Speko hangs up without the agent saying anything, and the session closes with `endReason: human_answered`. When a machine answers instead (voicemail, a phone menu, a hold queue or a call screener), the agent is unmuted and the call continues under `onMachine`; a person who then picks up from a call screener is hung up on too. `continue` (default) is a normal call. Applies to Realtime (`runMode: s2s`) phone calls only; a cascade call ignores it."
          },
          "voicemailMessage": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Spoken once into the mailbox under `onMachine: leave_message`, after the greeting ends. Plain text, rendered by the agent's TTS voice. On a Realtime (GPT-Live) call there is no TTS: the voice model says it in its own words, after it is shortened to fit the model's instruction limit — about 1,200 characters of English, fewer in Chinese, Japanese, Korean or Thai — at a sentence boundary where one exists."
          },
          "holdTimeoutSeconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 7200,
            "description": "An explicit cap, in seconds, on how long the call waits on hold: the call ends this long after anyone on the line last said something conversational, even while hold music plays. Menu prompts, queue announcements (\"your call is important to us\") and hold music do not reset it; a person speaking does. When unset, the call waits on hold for as long as the line is alive (speech, announcements or hold music), up to `maxDurationSeconds`, and ends only after 600 seconds with no speech and no sound at all. Set it to give up on a queue sooner. On cascade calls it applies once a phone menu is detected. On Realtime (`runMode: s2s`) phone calls a value of 600 or more applies to the whole call, and a smaller one applies once a phone menu is detected (or from the start with `profile: 'ivr'`), so a quiet caller on a regular call is not cut off early. It never extends a call past `maxDurationSeconds`, which stays the hard cap. A call it ends reports `endReason: hold_timeout`."
          },
          "keypadAllowedKeys": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "0",
                "1",
                "2",
                "3",
                "4",
                "5",
                "6",
                "7",
                "8",
                "9",
                "*",
                "#"
              ]
            },
            "minItems": 1,
            "maxItems": 12,
            "uniqueItems": true,
            "description": "Per-call override of the agent's value. The only keys the agent's keypad tool (`send_dtmf`) may press, from `0`-`9`, `*` and `#`. The tool's description lists them to the model, and a press that names any other key is refused (nothing is sent) and the model is told which keys are allowed. Omit to allow every key. Does not affect `outboundDtmf` or `telephony.dtmf`, which press exactly what you saved."
          },
          "machineBudgetSeconds": {
            "type": "integer",
            "minimum": 30,
            "maximum": 600,
            "default": 120,
            "description": "Per-call override of the agent's value. How long, in seconds, the call may stay on the line after it reaches a recordable voicemail greeting, or a phone's call-screening service that never puts a person through, before it ends with `endReason: dead_air`. Counted from the verdict. Defaults to 120. It does not cut a `leave_message` voicemail off mid-sentence: once the message starts, its own playout bounds it. Phone menus and hold queues are not bounded by it; see `holdTimeoutSeconds`. A voicemail ignored under `onMachine: continue` is not bounded by it. On a Realtime (GPT-Live) call a detected voicemail that runs it out ends as `voicemail_reached`."
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "The body of every error response. Branch on `code`, not on `error`: the message is for people and its wording can change. Some codes add fields of their own, documented on the endpoint that returns them (for example `bytes` and `limit` on `PIPELINE_CONFIG_TOO_LARGE`). A few endpoints, such as the voice library, nest their code as `{ \"error\": { \"code\": ... } }`; for codes in the catalog those responses also carry the top-level `code`, `retryable` and `docs_url`.",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message."
          },
          "code": {
            "type": "string",
            "description": "Machine-readable code. Stable across releases. `GET /v1/errors/{code}` explains it."
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether repeating the identical request could succeed. Present whenever the code is in the error catalog (`GET /v1/errors`)."
          },
          "docs_url": {
            "type": "string",
            "format": "uri",
            "description": "Documentation for this code. Present whenever `retryable` is."
          },
          "hint": {
            "type": "string",
            "description": "What to do about it. Present only for codes with a written explanation."
          },
          "issues": {
            "type": "array",
            "description": "Present on `VALIDATION_ERROR`: one entry per field that failed validation.",
            "items": {
              "type": "object",
              "required": [
                "path",
                "message"
              ],
              "properties": {
                "path": {
                  "type": "string",
                  "description": "Dot-separated path to the field, for example `telephony.ringTimeoutSeconds`. Empty for the body itself."
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "AgentStackPreferences": {
        "type": "object",
        "description": "Per-agent stack preferences. Empty / missing layers leave the router unconstrained for that layer; failover stays active within the allowed set.",
        "additionalProperties": false,
        "properties": {
          "allowedProviders": {
            "type": "object",
            "description": "Per-pipeline-layer provider allowlists (e.g. STT must be Deepgram or AssemblyAI). Each entry is either `\"<vendor>\"` (vendor wildcard — allow any model from that vendor) or `\"<vendor>:<model>\"` (allow only that specific model). Failover stays active across all entries in the layer.",
            "additionalProperties": false,
            "properties": {
              "stt": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed STT provider entry. Either a vendor id (e.g. `\"deepgram\"` — any Deepgram model) or `\"<vendor>:<model>\"` (e.g. `\"deepgram:nova-3\"` — only Nova-3, no fallback to other Deepgram models within the vendor). Both forms can be mixed in the same array.",
                  "examples": [
                    "deepgram",
                    "deepgram:nova-3",
                    "assemblyai"
                  ]
                },
                "description": "Allowed STT provider entries. Empty / absent = no constraint."
              },
              "llm": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed LLM provider entry. Either a vendor id (e.g. `\"openai\"` — any OpenAI model) or `\"<vendor>:<model>\"` (e.g. `\"openai:gpt-5\"` — only that model). Both forms can be mixed in the same array.",
                  "examples": [
                    "openai",
                    "openai:gpt-5",
                    "anthropic"
                  ]
                },
                "description": "Allowed LLM provider entries. Empty / absent = no constraint."
              },
              "tts": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed TTS provider entry. Either a vendor id (e.g. `\"elevenlabs\"` — any ElevenLabs model) or `\"<vendor>:<model>\"` (e.g. `\"elevenlabs:eleven_flash_v2_5\"` — only that model). Both forms can be mixed in the same array.",
                  "examples": [
                    "elevenlabs",
                    "elevenlabs:eleven_flash_v2_5",
                    "cartesia"
                  ]
                },
                "description": "Allowed TTS provider entries. Empty / absent = no constraint."
              },
              "s2s": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Allowed provider-direct S2S entry. Use a provider id or an exact provider:model pair.",
                  "examples": [
                    "openai:gpt-realtime-2.1",
                    "google:gemini-3.1-flash-live-preview",
                    "xai:grok-voice-latest"
                  ]
                },
                "description": "Allowed S2S provider entries. Empty / absent = no constraint."
              }
            }
          }
        }
      },
      "ProviderModelEntry": {
        "type": "object",
        "description": "A single allowlist-eligible entry returned by `/v1/providers/known`. The `id` is the verbatim string callers put into `allowedProviders` — either a vendor wildcard (`\"deepgram\"`) or a specific (vendor, model) pair (`\"deepgram:nova-3\"`).",
        "required": [
          "id",
          "vendor",
          "vendorName",
          "model",
          "benchmarked"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Verbatim string for `allowedProviders` entries. `\"<vendor>\"` for vendor-wildcard rows, `\"<vendor>:<model>\"` for model-specific rows.",
            "examples": [
              "deepgram",
              "deepgram:nova-3"
            ]
          },
          "vendor": {
            "type": "string",
            "description": "Lowercase vendor id.",
            "examples": [
              "deepgram"
            ]
          },
          "vendorName": {
            "type": "string",
            "description": "Human-readable vendor name.",
            "examples": [
              "Deepgram"
            ]
          },
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "Model id within the vendor, or `null` for the vendor-wildcard row.",
            "examples": [
              "nova-3"
            ]
          },
          "benchmarked": {
            "type": "boolean",
            "description": "Whether the router has benchmark data for this entry. `false` entries are visible but cannot win selection — including them in `allowedProviders` without a benchmarked fallback will fail to route."
          }
        }
      },
      "KnownProviders": {
        "type": "object",
        "description": "Per-modality lists of allowlist-eligible entries. Each layer's array contains one vendor-wildcard row per known vendor plus one row per known (vendor, model) pair. Use the `id` field as the verbatim string for `allowedProviders`.",
        "required": [
          "stt",
          "llm",
          "tts",
          "s2s"
        ],
        "properties": {
          "stt": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProviderModelEntry"
            }
          },
          "llm": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProviderModelEntry"
            }
          },
          "tts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProviderModelEntry"
            }
          },
          "s2s": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProviderModelEntry"
            }
          }
        }
      },
      "SessionEntries": {
        "type": "object",
        "required": [
          "entries"
        ],
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionEntry"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true,
            "description": "Cursor to pass to the next call. Null/absent when there are no more pages."
          }
        }
      },
      "SessionEntry": {
        "type": "object",
        "required": [
          "sessionId",
          "createdAt"
        ],
        "properties": {
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "description": "Lifecycle status of the session."
          },
          "endReason": {
            "type": "string",
            "nullable": true,
            "description": "Why the session closed. Null while live; set on sessions closed from 2026-10-21, plain hangups included (earlier plain hangups and normally completed Pipecat-runtime sessions can read null). When several causes race, the most specific one is kept: a remote hangup < `ended_by_api` < `agent_ended` < the voicemail and timeout reasons < never connected or failed. Normal ends (`status: ended`): `callee_hangup`, `caller_hangup`, `client_disconnected`, `agent_ended`, `voicemail_reached`, `machine_unavailable`, `normal_timeout`, `dead_air`, `hold_timeout`, `far_end_recording_loop`, `human_answered`, `max_duration`, `ended_by_api`, `sip_no_answer`, `caller_abandoned`, `stale_timeout`. Failures (`status: failed`): `sip_busy`, `sip_declined`, `sip_invalid_number`, `sip_carrier_rejected`, `sip_dial_rejected`, `originate_failed`, `no_agent_assigned`, `config_rejected`, `silent_bot_no_agent_audio`, `caller_not_connected`, `stt_stream_lost`, `llm_empty_completion`, `tts_empty_audio`, `provider_failed`, `credits_exhausted`, `worker_crash`, `worker_joined_late`, `agent_dispatch_failed`, `agent_never_joined`, `worker_response_timeout`, `key_vend_failed`, `machine_action_failed`, `unknown`. An open set: treat unrecognized values as other. Also returned by `GET /v1/sessions/{id}`."
          },
          "kind": {
            "type": "string",
            "description": "Session kind."
          },
          "pipelineConfig": {
            "type": "object",
            "description": "Snapshot of the pipeline config the session was minted with.",
            "properties": {
              "agentId": {
                "type": "string",
                "nullable": true,
                "description": "The agent the session was started against, if any. Filtered on by the `?agent=` query param."
              }
            }
          },
          "billing": {
            "type": "object",
            "nullable": true,
            "description": "Billing projection for provider-direct S2S. The create-time amount is estimated until Runtime exports canonical settlement evidence.",
            "properties": {
              "mode": {
                "type": "string",
                "examples": [
                  "direct_entitlement"
                ]
              },
              "state": {
                "type": "string",
                "enum": [
                  "estimated",
                  "final"
                ]
              },
              "estimatedCostMicroUsd": {
                "type": "string",
                "nullable": true
              },
              "finalCostMicroUsd": {
                "type": "string",
                "nullable": true
              },
              "evidence": {
                "type": "string",
                "nullable": true
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "recordingObjectPath": {
            "type": "string",
            "nullable": true,
            "description": "GCS object path of the recorded audio file, when a recording exists. Null while the recording is still being produced, when recording is suppressed for the organization, or when the upload failed. Use `GET /v1/sessions/{id}/recording` to fetch a short-lived signed URL — never construct one yourself."
          },
          "recordingDurationMs": {
            "type": "integer",
            "nullable": true,
            "description": "Duration of the finalized recording in milliseconds. Null until `recordingStatus` is `ready`."
          },
          "recordingStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pending",
              "uploading",
              "ready",
              "failed",
              "suppressed",
              "discarded"
            ],
            "description": "Lifecycle of this session's recording. `pending` after the call ends and before the agent worker has finished assembling the file. `uploading` while the file is being pushed to GCS. `ready` once it is fully persisted and downloadable via the signed-URL endpoint. `failed` if the upload errored — the recording is unrecoverable. `suppressed` for organizations whose `recordingEnabled` flag is false; no file was ever produced. `discarded` when the call never connected (`endReason` is a refused-dial reason — `sip_busy`, `sip_declined`, `sip_invalid_number`, `sip_carrier_rejected`, `sip_dial_rejected` — or `sip_no_answer`, or `caller_abandoned` when the inbound leg was still ringing), or when the recorder never received audio in a session where nobody spoke — the capture held only an empty room and is withheld. Null for provider-direct S2S and legacy sessions that predate recording."
          }
        }
      },
      "TurnIngest": {
        "type": "object",
        "required": [
          "index",
          "source",
          "text",
          "startedAt"
        ],
        "properties": {
          "index": {
            "type": "integer",
            "minimum": 0,
            "description": "Monotonically increasing position of this turn within the session, starting at 0. The `(session_id, index)` pair is the idempotency key — re-posting a turn with the same index replaces the prior entry."
          },
          "source": {
            "type": "string",
            "enum": [
              "user",
              "agent",
              "system"
            ],
            "description": "Who produced this turn. `user` = finalized STT from the caller. `agent` = finalized LLM output spoken by the agent. `system` = synthetic entries (e.g. session boundary markers)."
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 16384,
            "description": "Finalized transcript text. Interim STT partials are not accepted."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Wall-clock time at which the turn began, ISO 8601."
          },
          "endedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Wall-clock time at which the turn ended, ISO 8601. Null while the turn is still considered open (e.g. a streaming agent reply that hasn't fully closed)."
          },
          "provider": {
            "type": "string",
            "nullable": true,
            "description": "Provider that produced this turn (e.g. `deepgram`, `openai`). Null for `system` turns."
          },
          "model": {
            "type": "string",
            "nullable": true,
            "description": "Specific model id within the provider (e.g. `nova-3`, `gpt-4o-mini`). Null for `system` turns."
          }
        }
      },
      "TurnEntry": {
        "type": "object",
        "required": [
          "id",
          "index",
          "source",
          "text",
          "startedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Server-assigned id for this turn entry. Stable across reads."
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "description": "Monotonically increasing position of this turn within the session, starting at 0."
          },
          "source": {
            "type": "string",
            "enum": [
              "user",
              "agent",
              "system"
            ],
            "description": "Who produced this turn."
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 16384,
            "description": "Finalized transcript text."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Wall-clock time at which the turn began, ISO 8601."
          },
          "endedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Wall-clock time at which the turn ended, ISO 8601. Null if the turn is still open."
          },
          "provider": {
            "type": "string",
            "nullable": true,
            "description": "Provider that produced this turn. Null for `system` turns."
          },
          "model": {
            "type": "string",
            "nullable": true,
            "description": "Specific model id within the provider. Null for `system` turns."
          }
        }
      },
      "PostSessionTurnsRequest": {
        "type": "object",
        "required": [
          "turns"
        ],
        "properties": {
          "turns": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "description": "Batch of finalized turns to ingest. Workers should debounce (~200ms) and batch — single-turn POSTs are accepted but inefficient at scale.",
            "items": {
              "$ref": "#/components/schemas/TurnIngest"
            }
          }
        }
      },
      "SessionTranscript": {
        "type": "object",
        "required": [
          "entries"
        ],
        "properties": {
          "entries": {
            "type": "array",
            "description": "Finalized turns for this session, ordered by `index` ascending.",
            "items": {
              "$ref": "#/components/schemas/TurnEntry"
            }
          }
        }
      },
      "HumanCallLeg": {
        "type": "object",
        "description": "One participant's connection to a human call.",
        "properties": {
          "id": {
            "type": "string"
          },
          "controlId": {
            "type": "string",
            "description": "The command handle. Address every leg action to this."
          },
          "callId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "browser",
              "pstn",
              "agent"
            ],
            "description": "`browser` is a person on a softphone, `pstn` a phone number, `agent` an AI session bridged in."
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "internal"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "initiating",
              "ringing",
              "active",
              "held",
              "ended",
              "failed"
            ]
          },
          "brokerId": {
            "type": "string",
            "nullable": true,
            "description": "Owning org-defined broker, when `kind` is `browser`."
          },
          "phoneNumber": {
            "type": "string",
            "nullable": true,
            "description": "E.164 of the far end, when `kind` is `pstn`."
          },
          "muted": {
            "type": "boolean"
          },
          "onHold": {
            "type": "boolean"
          },
          "answeredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "endedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "endReason": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HumanCall": {
        "type": "object",
        "description": "A call between people. Distinct from an AI voice session — see `/v1/calls` for those.",
        "properties": {
          "id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "internal"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "initiating",
              "ringing",
              "active",
              "ended",
              "declined",
              "missed",
              "cancelled",
              "failed"
            ]
          },
          "answeredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "endedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "endReason": {
            "type": "string",
            "nullable": true
          },
          "legs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HumanCallLeg"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HumanCallJoinCredentials": {
        "type": "object",
        "description": "What a browser needs to join a room. Short-lived; minted per join, never cached.",
        "properties": {
          "token": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "Media transport URL to connect `livekit-client` to."
          },
          "identity": {
            "type": "string"
          },
          "roomName": {
            "type": "string"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HumanCallEvent": {
        "type": "object",
        "description": "One entry in a call's ordered event history. The same events (except `call.dtmf.received`) can be delivered as one-shot workspace webhooks.",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "call.initiated",
              "call.ringing",
              "call.answered",
              "call.bridged",
              "call.hold",
              "call.unhold",
              "call.mute",
              "call.unmute",
              "call.dtmf.received",
              "call.dtmf.sent",
              "call.transfer.initiated",
              "call.transfer.completed",
              "call.transfer.failed",
              "call.leg.hangup",
              "call.hangup"
            ]
          },
          "callId": {
            "type": "string"
          },
          "controlId": {
            "type": "string",
            "nullable": true,
            "description": "Null for call-scoped events (`call.initiated`, `call.hangup`)."
          },
          "payload": {
            "type": "object",
            "additionalProperties": true
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HumanCallDialRequest": {
        "type": "object",
        "required": [
          "brokerId",
          "to"
        ],
        "additionalProperties": false,
        "properties": {
          "brokerId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
          },
          "to": {
            "type": "string",
            "pattern": "^\\+[1-9]\\d{1,14}$",
            "description": "Destination, E.164."
          },
          "from": {
            "type": "string",
            "pattern": "^\\+[1-9]\\d{1,14}$",
            "description": "Caller ID. Must be a number this organization owns; defaults to the org's first outbound-capable number."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "HumanCallCommandRequest": {
        "type": "object",
        "description": "Body shape depends on the command. `answer`, `hold`, `unhold`, `mute`, `unmute` take an empty object. `hangup` accepts `reason`; `dtmf` requires `digits`; `bridge` requires `bridgeTo`; `transfer` requires `to` and `mode`. Unknown keys are rejected.",
        "properties": {
          "reason": {
            "type": "string",
            "maxLength": 120,
            "description": "`hangup` only. Recorded as the leg's `endReason`."
          },
          "digits": {
            "type": "string",
            "pattern": "^[0-9*#w,]{1,64}$",
            "description": "`dtmf` only. `0-9`, `*`, `#`, `,`, and `w` for a pause; max 64."
          },
          "bridgeTo": {
            "type": "string",
            "description": "`bridge` only. `controlId` of the leg to bridge this one to."
          },
          "to": {
            "type": "string",
            "description": "`transfer` only. An E.164 number, or the `controlId` of another broker's leg."
          },
          "mode": {
            "type": "string",
            "enum": [
              "blind",
              "warm"
            ],
            "description": "`transfer` only. `blind` hands off and drops the leg; `warm` opens a consultation room you complete with `bridge`."
          }
        }
      },
      "HumanCallCommandResult": {
        "type": "object",
        "description": "Every command resolves to the leg's post-command state, so a client never re-reads to learn what it just did.",
        "properties": {
          "leg": {
            "$ref": "#/components/schemas/HumanCallLeg"
          },
          "noop": {
            "type": "boolean",
            "description": "Present when the command changed nothing, e.g. muting a muted leg."
          }
        }
      },
      "BrokerPresence": {
        "type": "object",
        "description": "One broker's availability for inbound routing.",
        "properties": {
          "brokerId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "available",
              "busy",
              "away",
              "offline"
            ]
          },
          "lastSeenAt": {
            "type": "string",
            "format": "date-time"
          },
          "reachable": {
            "type": "boolean",
            "description": "False once the heartbeat is stale (90 s), regardless of `status`. Routing only rings reachable brokers."
          }
        }
      },
      "SmsSuppression": {
        "type": "object",
        "description": "A recipient the workspace must not message, and why.",
        "required": [
          "id",
          "recipient",
          "status",
          "suppressed_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "recipient": {
            "type": "string",
            "description": "E.164 number that opted out."
          },
          "status": {
            "type": "string",
            "description": "`suppressed` while the opt-out stands."
          },
          "keyword": {
            "type": [
              "string",
              "null"
            ],
            "description": "The keyword that triggered it, e.g. STOP."
          },
          "source_phone_number_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_message_provider_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "suppressed_at": {
            "type": "string",
            "format": "date-time"
          },
          "lifted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set when the suppression was lifted."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TtsCatalogMeta": {
        "type": "object",
        "properties": {
          "benchmark": {
            "type": "object",
            "properties": {
              "schema": {
                "type": "integer"
              },
              "routing_policy_version": {
                "type": "string"
              }
            }
          },
          "quality_overlay": {
            "type": "object",
            "properties": {
              "generated_at": {
                "type": "string"
              },
              "run_id": {
                "type": "string"
              }
            }
          },
          "org_routable_vendors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "coverage": {
            "type": "object",
            "properties": {
              "models_routable": {
                "type": "integer"
              },
              "models_measured": {
                "type": "integer"
              },
              "voices_routable": {
                "type": "integer"
              },
              "voices_measured": {
                "type": "integer"
              }
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The caveats that stop a number being read as something it is not."
          }
        }
      },
      "TtsLatency": {
        "type": "object",
        "description": "TTS latency detail, published beside `latency_ms` and `latency_evidence` rather than in place of them. The blocks are different quantities, so do not compare them with each other or with `latency_ms`.",
        "properties": {
          "vendor_direct": {
            "type": "array",
            "description": "The reading in each region it covers. The row whose `location` equals `latency_evidence.region` is the same number as `latency_ms`; the others are the regional siblings the ranked field does not carry.",
            "items": {
              "type": "object",
              "properties": {
                "region": {
                  "type": "string"
                },
                "location": {
                  "type": "string"
                },
                "p50_ms": {
                  "type": "number"
                },
                "p90_ms": {
                  "type": "number",
                  "nullable": true
                }
              }
            }
          },
          "per_language": {
            "type": "array",
            "description": "Per-language time to first audio, p50 and p90. Measured differently from `latency_ms`, so these rows compare with each other and never against it. `n`, `sweeps` and `wide_tail` describe each row on its own terms.",
            "items": {
              "type": "object",
              "properties": {
                "language": {
                  "type": "string"
                },
                "p50": {
                  "type": "number"
                },
                "p90": {
                  "type": "number"
                },
                "n": {
                  "type": "integer"
                },
                "sweeps": {
                  "type": "integer"
                },
                "wide_tail": {
                  "type": "boolean",
                  "description": "p90/p50 above 1.7, a tail this row’s sample size cannot resolve. Do not read movement between sweeps as change."
                },
                "voice": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "TtsMetricValue": {
        "type": "object",
        "required": [
          "value",
          "unit",
          "higher_is_better"
        ],
        "description": "One measured axis, with its unit and direction stated so a caller never has to infer them.",
        "properties": {
          "value": {
            "type": "number"
          },
          "unit": {
            "type": "string",
            "description": "`elo_arena`, `score_0_1`, `mos_0_5`, `percent`."
          },
          "higher_is_better": {
            "type": "boolean"
          },
          "board": {
            "type": "string"
          },
          "ci": {
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "n": {
            "type": "integer"
          },
          "basis": {
            "type": "string",
            "description": "Robustness fixture basis. Do not read small gaps across different bases as real."
          },
          "unjudgeable": {
            "type": "integer"
          },
          "field_mean": {
            "type": "number",
            "description": "Naturalness only: 1500, the per-language field average."
          },
          "comparable_across_languages": {
            "type": "boolean",
            "description": "Naturalness only, and always false. Arena Elo is fit per language."
          },
          "estimated": {
            "type": "boolean",
            "description": "Present and true when the score came from Speko’s own scorer rather than human votes."
          }
        }
      },
      "TtsModel": {
        "type": "object",
        "required": [
          "id",
          "provider",
          "model",
          "routable",
          "benchmark"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "cartesia:sonic-3.5"
          },
          "provider": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "routable": {
            "type": "boolean"
          },
          "routable_via": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform_key",
              "byok",
              null
            ]
          },
          "benchmark": {
            "type": "object",
            "properties": {
              "language": {
                "type": "string"
              },
              "board": {
                "type": "string"
              },
              "naturalness": {
                "$ref": "#/components/schemas/TtsMetricValue"
              },
              "robustness": {
                "$ref": "#/components/schemas/TtsMetricValue"
              },
              "panel_mos": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TtsMetricValue"
                  }
                ],
                "description": "A 0-5 direct-rating scale. `naturalness` uses a different scale."
              },
              "panel_named_pct": {
                "$ref": "#/components/schemas/TtsMetricValue"
              },
              "latency_ms": {
                "type": "number",
                "nullable": true,
                "description": "The ranked reading: first-audio p50. The only latency to sort on. Null where the model has no reading."
              },
              "latency_evidence": {
                "type": "object",
                "description": "The region and sample size behind `latency_ms`. Present whenever `latency_ms` is non-null.",
                "properties": {
                  "board": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string"
                  },
                  "n": {
                    "type": "integer"
                  },
                  "p90_ms": {
                    "type": "number"
                  }
                }
              },
              "latency": {
                "$ref": "#/components/schemas/TtsLatency"
              },
              "cost": {
                "type": "object",
                "properties": {
                  "per_1m_chars_usd": {
                    "type": "number",
                    "nullable": true
                  },
                  "per_min_usd": {
                    "type": "number",
                    "nullable": true,
                    "description": "Derived, not measured: `per_1m_chars_usd x 900 / 1e6`."
                  },
                  "higher_is_better": {
                    "type": "boolean"
                  }
                },
                "description": "Always present. On a model with no board row every number is null, which is a different answer from the field being absent."
              }
            }
          },
          "languages": {
            "type": "object",
            "properties": {
              "measured": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Language tags this model is measured on."
              },
              "vendor_claimed": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The vendor lists the language but we have not probed it."
              },
              "count": {
                "type": "integer"
              }
            }
          },
          "voices": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "measured": {
                "type": "integer",
                "description": "How many of those voices carry a measurement."
              },
              "compatibility": {
                "type": "string",
                "enum": [
                  "verified",
                  "vendor_roster",
                  "restricted"
                ]
              }
            },
            "description": "How many voices can be used with this model, how many of those carry a measurement, and how the pairing was established. Pass `?model=` to `/v1/tts/voices` to list them."
          }
        }
      },
      "TtsVoice": {
        "type": "object",
        "required": [
          "id",
          "vendor",
          "voice_id",
          "name",
          "use_with",
          "model_id",
          "quality"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "cartesia:f9836c6e-a0bd-460e-9d3c-f7299fa60f94"
          },
          "vendor": {
            "type": "string"
          },
          "voice_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "gender": {
            "type": "string",
            "nullable": true
          },
          "accent": {
            "type": "string",
            "nullable": true
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "styles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Descriptive words only. Measured quality is in `quality.labels`."
          },
          "use_with": {
            "type": "object",
            "description": "The exact triple to send to `POST /v1/synthesize` or `POST /v1/sessions`. Pass it through unmodified.",
            "required": [
              "provider",
              "model",
              "voice"
            ],
            "properties": {
              "provider": {
                "type": "string"
              },
              "model": {
                "type": "string"
              },
              "voice": {
                "type": "string"
              }
            }
          },
          "relay_provider": {
            "type": "string",
            "description": "`use_with.provider` as `router.speko.dev` spells it, for `routing.provider` there. It differs only for Gemini (`gemini`) and Fish Audio (`fish`). It names the vendor and does not claim the Router serves `use_with.model`."
          },
          "model_id": {
            "type": "string"
          },
          "voice_compatibility": {
            "type": "string",
            "enum": [
              "verified",
              "vendor_roster",
              "restricted"
            ],
            "description": "`verified` = this voice/model pairing is individually confirmed. `vendor_roster` = same vendor, pairing inferred from vendor-level voice ownership. `restricted` = the model accepts only a documented subset of its vendor’s voices."
          },
          "quality": {
            "type": "object",
            "required": [
              "verdict",
              "labels"
            ],
            "properties": {
              "verdict": {
                "type": "string",
                "enum": [
                  "measured",
                  "measured_no_labels",
                  "unmeasured"
                ],
                "description": "`measured_no_labels` means we measured the voice and it cleared nothing. `unmeasured` means we never measured it. Only positive labels are emitted, so an empty `labels` array is a result in the first case and a gap in the second."
              },
              "basis": {
                "type": "string",
                "nullable": true,
                "description": "`longform-render` or `preview-clip`. Several axes are only computable from a long-form render."
              },
              "labels": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Human display strings, e.g. `[\"no hiss\",\"steady to the end\",\"10k chars\"]`, in canonical chip order. The cumulative capacity rungs collapse to the highest one held."
              },
              "summary": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "max_chars_per_call": {
                "type": "integer",
                "nullable": true
              },
              "words_per_minute": {
                "type": "number",
                "nullable": true
              }
            }
          },
          "why": {
            "type": "object",
            "nullable": true,
            "description": "Generated from deterministic templates over the measured numbers. No model writes this copy.",
            "properties": {
              "best_for": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "strengths": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "weaknesses": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "tradeoffs": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "RecordingUrl": {
        "type": "object",
        "required": [
          "url",
          "expiresAt"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Short-lived download link for the recording. Fetch it right away; request a new one once it expires."
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When `url` stops working (about 5 minutes after the request). Fetch this endpoint again for a fresh URL."
          }
        }
      },
      "CallReportTranscriptEntry": {
        "type": "object",
        "description": "One transcript turn in a call report (`GET /v1/calls/{id}/report` and report webhooks). Keys are camelCase here, unlike `CallTranscriptEntry` on `GET /v1/calls/{id}`.",
        "required": [
          "id",
          "index",
          "source",
          "text",
          "startedAt",
          "endedAt",
          "provider",
          "model",
          "metadata"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "index": {
            "type": "integer"
          },
          "source": {
            "type": "string",
            "enum": [
              "user",
              "agent",
              "system"
            ]
          },
          "text": {
            "type": "string"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "$ref": "#/components/schemas/JsonObject"
          }
        }
      },
      "AgentVersion": {
        "type": "object",
        "required": [
          "id",
          "organization_id",
          "agent_id",
          "version_number",
          "status",
          "session_config",
          "briefing_markdown",
          "briefing_md",
          "source",
          "created_by_user_id",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Version id."
          },
          "organization_id": {
            "type": "string"
          },
          "agent_id": {
            "type": "string",
            "description": "Agent id, prefixed `agent_`."
          },
          "version_number": {
            "type": "integer",
            "minimum": 1,
            "description": "Per-agent sequence number. A draft takes the next number when it is opened, so it can sit below live if something else is published first."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "live",
              "archived"
            ],
            "description": "`draft` serves no calls, `live` is what every call gets (at most one), `archived` is a previous live version."
          },
          "session_config": {
            "type": "object",
            "additionalProperties": true,
            "description": "The config snapshot this version serves."
          },
          "briefing_markdown": {
            "type": [
              "string",
              "null"
            ],
            "description": "Briefing supplied at deploy time."
          },
          "briefing_md": {
            "type": [
              "string",
              "null"
            ],
            "description": "Alias of `briefing_markdown`."
          },
          "source": {
            "type": "string",
            "description": "What cut this version, for example `deploy`, `mcp`, `draft`, `fix:<runId>` or `rollback:<n>`. A caller-supplied `source` on deploy is stored verbatim."
          },
          "created_by_user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AgentVersionDeployRequest": {
        "type": "object",
        "properties": {
          "session_config": {
            "type": "object",
            "additionalProperties": true,
            "description": "Publish this config instead of the draft or the current agent. `sttOptions`/`stt_options`, `ttsOptions`/`tts_options` and `audioOutput`/`audio_output` are validated strictly (400 on an invalid value); other keys are stored as given. The agent's `name`, `systemPrompt`, `voice`, `runtime`, `runMode`, `intent`, `llmOptions`, `stackPreferences`, `sttOptions`, `ttsOptions`, `speechNormalization` and `audioOutput` are updated from it."
          },
          "sessionConfig": {
            "type": "object",
            "additionalProperties": true,
            "description": "Alias of `session_config`; `session_config` wins when both are sent."
          },
          "briefing_markdown": {
            "type": "string",
            "description": "Stored on the new live version."
          },
          "briefing_md": {
            "type": "string",
            "description": "Alias of `briefing_markdown`; `briefing_markdown` wins when both are sent."
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Recorded on the version. Defaults to `deploy` when a draft is published and `mcp` otherwise."
          },
          "override": {
            "type": "boolean",
            "description": "Acknowledge and bypass a reliability-gate block."
          },
          "override_ack": {
            "type": "boolean",
            "description": "Alias of `override`."
          }
        }
      },
      "AgentVersionDeployResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AgentVersion"
          },
          {
            "type": "object",
            "required": [
              "published_draft"
            ],
            "properties": {
              "published_draft": {
                "type": "boolean",
                "description": "True when the pending draft was promoted to live."
              }
            }
          }
        ]
      },
      "AgentVersionRollbackRequest": {
        "type": "object",
        "required": [
          "target_version_number"
        ],
        "properties": {
          "target_version_number": {
            "type": "integer",
            "minimum": 1,
            "description": "The version whose config and graph to restore."
          },
          "override": {
            "type": "boolean",
            "description": "Acknowledge and bypass a reliability-gate block."
          },
          "override_ack": {
            "type": "boolean",
            "description": "Alias of `override`."
          }
        }
      },
      "AgentDraft": {
        "type": "object",
        "required": [
          "agent_id",
          "has_draft",
          "changed_fields",
          "config",
          "draft_version_number",
          "live_version_number",
          "stale",
          "updated_at",
          "updated_by_user_id"
        ],
        "properties": {
          "agent_id": {
            "type": "string",
            "description": "Agent id, prefixed `agent_`."
          },
          "has_draft": {
            "type": "boolean",
            "description": "A draft exists. It can be true with no `changed_fields` when the draft only changes the workflow graph."
          },
          "changed_fields": {
            "type": "array",
            "description": "Config fields the draft would change on deploy, compared with the live version (or with the agent itself when it has never been deployed).",
            "items": {
              "type": "string",
              "enum": [
                "name",
                "systemPrompt",
                "voice",
                "voiceInstructions",
                "firstMessage",
                "inboundFirstMessage",
                "outboundFirstMessage",
                "timezone",
                "intent",
                "llmOptions",
                "stackPreferences",
                "sttOptions",
                "ttsOptions",
                "runMode",
                "runtime",
                "backgroundAudio",
                "audioOutput",
                "speechNormalization",
                "idleRePrompts",
                "sttMissRecovery",
                "turnHandling",
                "languageRouting"
              ]
            }
          },
          "config": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "The draft's full config snapshot, keyed by agent field name. Null when there is no draft."
          },
          "draft_version_number": {
            "type": [
              "integer",
              "null"
            ]
          },
          "live_version_number": {
            "type": [
              "integer",
              "null"
            ]
          },
          "stale": {
            "type": "boolean",
            "description": "Another version went live after this draft was opened, so the draft's version number is below live. It still deploys."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the draft was opened. Later saves do not change it."
          },
          "updated_by_user_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The user who last saved the draft. Null for API-key saves and when there is no draft."
          }
        }
      },
      "SessionEndResult": {
        "type": "object",
        "required": [
          "ok",
          "status"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "status": {
            "type": "string",
            "enum": [
              "ending",
              "ended",
              "already_ended"
            ],
            "description": "`ending` (HTTP 202) = room deleted, close pending on the room webhook. `ended` = closed by this request. `already_ended` = closed before this request."
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the session closed. Absent when `status` is `ending`."
          }
        }
      },
      "VoiceSearchRequest": {
        "type": "object",
        "description": "At least one of `description`, `similarTo` or `filters` is required.",
        "anyOf": [
          {
            "required": [
              "description"
            ]
          },
          {
            "required": [
              "similarTo"
            ]
          },
          {
            "required": [
              "filters"
            ]
          }
        ],
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Free-text description of the voice. Ranked by a lexical score, blended with embedding similarity when an embedding is available."
          },
          "similarTo": {
            "type": "object",
            "required": [
              "vendor",
              "voiceId"
            ],
            "description": "Rank by similarity to this indexed voice. Results keep only voices of the reference's gender when the reference has one.",
            "properties": {
              "vendor": {
                "type": "string"
              },
              "voiceId": {
                "type": "string"
              }
            }
          },
          "filters": {
            "$ref": "#/components/schemas/VoiceSearchFilters"
          },
          "topK": {
            "type": "integer",
            "minimum": 1,
            "maximum": 600,
            "default": 10,
            "description": "Maximum number of results."
          }
        }
      },
      "VoiceSearchFilters": {
        "type": "object",
        "description": "Exact filters, all ANDed.",
        "properties": {
          "language": {
            "type": "string",
            "description": "A bare tag (`en`) also matches regional tags (`en-US`); a regional tag matches exactly."
          },
          "gender": {
            "type": "string",
            "enum": [
              "male",
              "female",
              "neutral"
            ]
          },
          "vendor": {
            "type": "string"
          },
          "accent": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40,
            "description": "Case-insensitive."
          },
          "style": {
            "type": "string",
            "description": "Exact match against one entry of `styles`."
          },
          "routerPinnable": {
            "type": "boolean"
          }
        }
      },
      "VoiceSearchResult": {
        "type": "object",
        "required": [
          "vendor",
          "voiceId",
          "name",
          "gender",
          "languages",
          "accent",
          "description",
          "styles",
          "previewUrl",
          "attach",
          "routerPinnable",
          "provenance"
        ],
        "properties": {
          "vendor": {
            "type": "string"
          },
          "voiceId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "gender": {
            "type": [
              "string",
              "null"
            ]
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "accent": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Vendor prose about the voice."
          },
          "styles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Descriptive style words plus `qa:`-prefixed measured quality tokens."
          },
          "previewUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "attach": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Models this voice is indexed against (the profile's `models`)."
          },
          "routerPinnable": {
            "type": "boolean"
          },
          "provenance": {
            "type": "string",
            "description": "Where the index row came from."
          },
          "score": {
            "type": "number",
            "description": "Ranking score. Present for `description` and `similarTo` searches, absent for filter-only searches (which sort by vendor, then voice id)."
          }
        }
      },
      "VoiceSearchResponse": {
        "type": "object",
        "required": [
          "results",
          "total",
          "facets",
          "indexedTotal"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VoiceSearchResult"
            }
          },
          "total": {
            "type": "integer",
            "description": "Voices that matched before `topK` was applied."
          },
          "facets": {
            "type": "object",
            "required": [
              "providers",
              "accents"
            ],
            "description": "Counts under the request's filters. Each facet ignores its own filter, so `providers` counts every vendor even when `filters.vendor` is set, and likewise for `accents`.",
            "properties": {
              "providers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "vendor",
                    "count"
                  ],
                  "properties": {
                    "vendor": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              },
              "accents": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "accent",
                    "count"
                  ],
                  "properties": {
                    "accent": {
                      "type": "string",
                      "description": "Lowercased."
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "indexedTotal": {
            "type": "integer",
            "description": "Size of the whole voice index."
          }
        }
      },
      "VoiceSearchProfile": {
        "type": "object",
        "required": [
          "id",
          "vendor",
          "voiceId",
          "name",
          "gender",
          "languages",
          "accent",
          "styles",
          "description",
          "previewUrl",
          "models",
          "routerModels",
          "source",
          "provenance",
          "routerPinnable",
          "verifiedAt",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Voice-index row id."
          },
          "vendor": {
            "type": "string"
          },
          "voiceId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "gender": {
            "type": [
              "string",
              "null"
            ]
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "accent": {
            "type": [
              "string",
              "null"
            ]
          },
          "styles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Descriptive style words plus `qa:`-prefixed measured quality tokens."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "previewUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "routerModels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "provenance": {
            "type": "string"
          },
          "routerPinnable": {
            "type": "boolean"
          },
          "verifiedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "VoiceSearchNotFoundError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "VOICE_NOT_FOUND"
                ]
              }
            }
          }
        }
      },
      "ErrorCatalogEntry": {
        "type": "object",
        "required": [
          "code",
          "category",
          "retryable",
          "documented",
          "docs_url"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "auth",
              "not_found",
              "validation",
              "quota",
              "provider",
              "state",
              "other"
            ]
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether repeating the identical request could succeed."
          },
          "documented": {
            "type": "boolean",
            "description": "True when `meaning` was written by a person; false when only the category is known."
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          },
          "meaning": {
            "type": "string",
            "description": "What happened. Absent when not yet written."
          },
          "hint": {
            "type": "string",
            "description": "What to do about it. Absent for most codes."
          }
        }
      },
      "ErrorCatalog": {
        "type": "object",
        "required": [
          "coverage",
          "errors"
        ],
        "properties": {
          "coverage": {
            "type": "object",
            "description": "How many codes carry a written explanation, overall and per category.",
            "required": [
              "total",
              "documented",
              "byCategory"
            ],
            "properties": {
              "total": {
                "type": "integer"
              },
              "documented": {
                "type": "integer"
              },
              "byCategory": {
                "type": "object",
                "additionalProperties": {
                  "type": "object",
                  "required": [
                    "total",
                    "documented"
                  ],
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "documented": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorCatalogEntry"
            }
          }
        }
      },
      "ErrorCodeDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorCatalogEntry"
          },
          {
            "type": "object",
            "required": [
              "sources"
            ],
            "properties": {
              "sources": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Server source files that return this code."
              }
            }
          }
        ]
      }
    },
    "responses": {
      "SmsSendingNotApproved": {
        "description": "No number in the workspace is on an approved 10DLC campaign (code SMS_SENDING_NOT_APPROVED). Register under Settings > SMS compliance and assign a number to the approved campaign.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "Sending SMS requires an approved 10DLC registration for this workspace. Register at https://platform.speko.ai/settings/sms-compliance.",
                  "code": "SMS_SENDING_NOT_APPROVED"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid bearer token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "Unauthorized",
                  "code": "UNAUTHORIZED"
                }
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request body or query parameters failed validation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "Invalid request",
                  "code": "VALIDATION_ERROR",
                  "issues": [
                    {
                      "path": "to",
                      "message": "Required"
                    }
                  ],
                  "retryable": false,
                  "docs_url": "https://docs.speko.dev/errors/validation_error"
                }
              }
            }
          }
        }
      },
      "InvalidAudio": {
        "description": "Empty body, missing/invalid `x-speko-intent` header, or malformed `x-speko-constraints`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "Audio body is empty.",
                  "code": "INVALID_AUDIO"
                }
              }
            }
          }
        }
      },
      "TranscribeFailed": {
        "description": "No STT provider available for the intent, or every candidate failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "All providers failed",
                  "code": "TRANSCRIBE_FAILED"
                }
              }
            }
          }
        }
      },
      "SynthesizeFailed": {
        "description": "No TTS provider available for the intent, no default voice for chosen provider, or every candidate failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "All providers failed",
                  "code": "SYNTHESIZE_FAILED"
                }
              }
            }
          }
        }
      },
      "CompleteFailed": {
        "description": "No LLM provider available for the intent, or every candidate failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "All providers failed",
                  "code": "COMPLETE_FAILED"
                }
              }
            }
          }
        }
      },
      "SessionCreateFailed": {
        "description": "Token mint failed, agent dispatch failed, or DB insert failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "Failed to dispatch voice agent",
                  "code": "SESSION_CREATE_FAILED"
                }
              }
            }
          }
        }
      },
      "AgentNotFound": {
        "description": "No agent with this id exists in the authenticated organization.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "not found",
                  "code": "NOT_FOUND"
                }
              }
            }
          }
        }
      },
      "AgentDeleteConflict": {
        "description": "The organization's only remaining agent cannot be deleted.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "Cannot delete the only agent in this organization",
                  "code": "LAST_AGENT"
                }
              }
            }
          }
        }
      },
      "HumanCallingUnavailable": {
        "description": "Human calling is not enabled for this organization (it is in early access — ask us to enable it), or is not configured on this deployment.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "default": {
                "value": {
                  "error": "human calling is not enabled for this organization",
                  "code": "HUMAN_CALLING_DISABLED"
                }
              }
            }
          }
        }
      }
    }
  }
}