{
  "openapi": "3.1.0",
  "info": {
    "title": "Voxa API",
    "version": "0.1.0",
    "description": "Manage assistants, read call logs and recordings, and start browser calls from your own server. The human-readable reference with curl examples is [/api.html](/api.html).\n\n**Authentication.** Send a secret key from the console's API keys page as `Authorization: Bearer sk_live_…`. Keep it on your server; browsers use the organization's public key with the *Client (public key)* routes.\n\n**Scopes.** A key has `read` (GET, HEAD) and/or `write` (POST, PATCH, DELETE); each operation's security requirement names the scope it needs. A missing scope answers 403.\n\n**IP allowlists.** A key may be limited to addresses and CIDR ranges (console, API keys page). A request from any other address answers 403 `{ \"error\": \"This key cannot be used from <ip>\", \"reason\": \"ip_not_allowed\" }` before the rate limit counts it.\n\n**Errors.** JSON `{ \"error\": string, \"fields\"?: { [field]: string } }`; `fields` names each invalid field of a 400.\n\n**Pagination.** Lists answer `{ data, count, total, hasMore, nextCursor }` and take `limit` (1..100, default 50; /calls up to 200) and `cursor` (the previous page's nextCursor).\n\n**Rate limits.** 120 requests per minute per key; every response carries X-RateLimit-Limit and X-RateLimit-Remaining, a 429 also Retry-After (seconds).\n\n**Conventions.** Every /api/v1 response has permissive CORS headers and OPTIONS answers preflights (not listed per path). Methods not listed for a path answer 405 with an Allow header. Records are scoped to the key's organization. Fields may be added to responses without notice; removing or renaming one ships as /api/v2.\n\n**Webhooks.** Signed with the organization's secret: `x-voxa-signature: sha256=<hex HMAC-SHA256 of \"${x-voxa-timestamp}.${rawBody}\">`. Reject timestamps older than 5 minutes."
  },
  "servers": [
    {
      "url": "/",
      "description": "The console that serves this document"
    }
  ],
  "tags": [
    {
      "name": "Assistants",
      "description": "Voice agent configurations."
    },
    {
      "name": "Calls",
      "description": "Call logs, transcripts, recordings, and browser calls started from your server."
    },
    {
      "name": "Do-not-call",
      "description": "Numbers campaigns never dial."
    },
    {
      "name": "Privacy",
      "description": "Erasure of a caller's data on request."
    },
    {
      "name": "Campaigns",
      "description": "Outbound campaigns: list them, add contacts, start the dialer."
    },
    {
      "name": "Automation",
      "description": "REST hooks for Zapier, Make and n8n: subscribe a URL to an event, unsubscribe it. Deliveries are signed like webhooks."
    },
    {
      "name": "Knowledge",
      "description": "Knowledge files and bases (read-only)."
    },
    {
      "name": "Numbers",
      "description": "Phone numbers (read-only)."
    },
    {
      "name": "Tools",
      "description": "Tools assistants can call (read-only)."
    },
    {
      "name": "Analytics",
      "description": "Call totals for a window (read-only)."
    },
    {
      "name": "Logs",
      "description": "The organization's log entries, redacted (read-only)."
    },
    {
      "name": "Account",
      "description": "The key itself."
    },
    {
      "name": "Meta",
      "description": "This document."
    },
    {
      "name": "Client (public key)",
      "description": "Routes the widget and the web SDK call from browsers with the organization's public key (pk_...) instead of a secret key. They live outside /api/v1, answer CORS preflights, follow the organization's allowed origins and are rate limited per address and per key. Errors are `{ error, reason? }`."
    },
    {
      "name": "Webhooks",
      "description": "Events Voxa POSTs to your server URL."
    }
  ],
  "paths": {
    "/api/v1/assistants": {
      "get": {
        "operationId": "listAssistants",
        "tags": [
          "Assistants"
        ],
        "summary": "List assistants",
        "description": "Every assistant of the key's organization (assistants created inline by the web SDK are left out), oldest first. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssistantList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createAssistant",
        "tags": [
          "Assistants"
        ],
        "summary": "Create an assistant",
        "description": "Create from a partial body; the console's defaults fill the rest. Price and latency are computed. Created Published, the configuration becomes version 1. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssistantCreate"
              },
              "example": {
                "name": "Taylor - Bookings",
                "provider": "Anthropic",
                "model": "claude-sonnet-4",
                "firstMessage": "Hi, you have reached Acme Dental. How can I help?",
                "tools": {
                  "book": true
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new assistant.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Assistant"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, or validation failed: `fields` names each unknown, read-only or invalid field.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No `write` scope, or the key's IP allowlist refuses the address; or a test key (sk_test_) changed a compliance setting (hipaa, recording, recordingNotice, recordingNoticeText, redaction, serverUrl, followUpSms, payments; `fields` names them). Live keys are created by admins and may change them.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/assistants/{id}": {
      "get": {
        "operationId": "getAssistant",
        "tags": [
          "Assistants"
        ],
        "summary": "Get an assistant",
        "description": "Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Assistant id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Assistant"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No assistant with that id in the key's organization.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "operationId": "updateAssistant",
        "tags": [
          "Assistants"
        ],
        "summary": "Update an assistant",
        "description": "Change any writable fields; `tools` is merged with the current value. Draft → Published saves the configuration as a new version. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Assistant id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssistantUpdate"
              },
              "example": {
                "status": "Published",
                "maxDuration": 15,
                "tools": {
                  "query": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated assistant.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Assistant"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, or validation failed (`fields`).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No `write` scope, or the key's IP allowlist refuses the address; or a test key (sk_test_) changed a compliance setting (hipaa, recording, recordingNotice, recordingNoticeText, redaction, serverUrl, followUpSms, payments; `fields` names them). Live keys are created by admins and may change them.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No assistant with that id in the key's organization.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`hipaa: true` while video agents run on the assistant and the organization has no operator-confirmed video BAA (`fields.hipaa` names them).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deleteAssistant",
        "tags": [
          "Assistants"
        ],
        "summary": "Delete an assistant",
        "description": "Same cleanup as the console: numbers it answered become unassigned; squads, workflows, campaigns and test suites lose the reference. Its calls are kept. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Assistant id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No assistant with that id in the key's organization.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/calls": {
      "get": {
        "operationId": "listCalls",
        "tags": [
          "Calls"
        ],
        "summary": "List calls",
        "description": "Newest first, without transcripts. Calls are listed from the moment they start (status live). Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "assistantId",
            "in": "query",
            "description": "Only calls answered by this assistant.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "Only calls started at or after this time: ISO-8601 or a unix timestamp in seconds or milliseconds.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..200 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (since, limit or cursor): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/calls/{id}": {
      "get": {
        "operationId": "getCall",
        "tags": [
          "Calls"
        ],
        "summary": "Get a call",
        "description": "One call with its transcript. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Call id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No call with that id in the key's organization.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/calls/{id}/recording": {
      "get": {
        "operationId": "getCallRecording",
        "tags": [
          "Calls"
        ],
        "summary": "Download a call recording",
        "description": "The recording as audio/wav (16-bit PCM, mono, 24 kHz). With bucket storage: 302 to a signed URL valid for at least 15 minutes (follow redirects, do not store it); on local disk: streamed, with single byte-range support. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Call id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "download",
            "in": "query",
            "description": "1 asks for an attachment named {id}.wav.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "A single byte range, e.g. bytes=0-1023.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The WAV file.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "audio/wav": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested byte range.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "audio/wav": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "302": {
            "description": "Redirect to a signed URL on the storage bucket.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              }
            }
          },
          "400": {
            "description": "Invalid call id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No call with that id, or the call has no recording.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "416": {
            "description": "The range cannot be satisfied.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "head": {
        "operationId": "headCallRecording",
        "tags": [
          "Calls"
        ],
        "summary": "Check a call recording",
        "description": "The recording as audio/wav (16-bit PCM, mono, 24 kHz). With bucket storage: 302 to a signed URL valid for at least 15 minutes (follow redirects, do not store it); on local disk: streamed, with single byte-range support. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Call id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "download",
            "in": "query",
            "description": "1 asks for an attachment named {id}.wav.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "A single byte range, e.g. bytes=0-1023.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The WAV file.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "audio/wav": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested byte range.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "audio/wav": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "302": {
            "description": "Redirect to a signed URL on the storage bucket.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              }
            }
          },
          "400": {
            "description": "Invalid call id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No call with that id, or the call has no recording.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "416": {
            "description": "The range cannot be satisfied.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/web-calls": {
      "post": {
        "operationId": "createWebCall",
        "tags": [
          "Calls"
        ],
        "summary": "Start a web call",
        "description": "A short-lived call token and the voice server's WebSocket URL for a browser to connect to. Hand both to the browser. `customerId` names the caller in your own system: it is stored on the call and lets assistants with memory on recognize a returning caller (minted with your secret key, it is the trusted way to pass it). Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebCallCreate"
              },
              "example": {
                "assistantId": "asst_7f3a91c2",
                "customerId": "cus_8f2k1"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "A call token.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebCall"
                },
                "example": {
                  "token": "eyJhc3Npc3RhbnRJZCI6...",
                  "url": "ws://localhost:3001",
                  "expiresIn": 300,
                  "assistantId": "asst_7f3a91c2"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing assistantId or a malformed customerId (`fields`).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The organization cannot make calls right now: no credits left or monthly limit reached (reason no_credits / monthly_limit); with a test key, the test budget has no test call's minutes left (reason test_budget), or the organization has neither credits nor its own model provider key (reason no_credits).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No assistant with that id in the key's organization.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The key's rate limit, or a call cap: the organization's concurrent calls (reason concurrency_limit) or the assistant's daily budget (reason daily_budget). Wait Retry-After seconds.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/dnc": {
      "get": {
        "operationId": "listDnc",
        "tags": [
          "Do-not-call"
        ],
        "summary": "List do-not-call numbers",
        "description": "Newest first. Campaigns never dial a listed number. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Matches the number's digits or the reason.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DncEntryList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "addDnc",
        "tags": [
          "Do-not-call"
        ],
        "summary": "Add a number to the do-not-call list",
        "description": "Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DncCreate"
              },
              "example": {
                "number": "+14155550100",
                "reason": "Opted out by email"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The number was on the list already: the stored entry.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DncEntry"
                }
              }
            }
          },
          "201": {
            "description": "Added (source api).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DncEntry"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, not a phone number, or a reason that is too long (`fields`).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/dnc/{id}": {
      "get": {
        "operationId": "getDncEntry",
        "tags": [
          "Do-not-call"
        ],
        "summary": "Get a do-not-call entry",
        "description": "Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Entry id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DncEntry"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No entry with that id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deleteDncEntry",
        "tags": [
          "Do-not-call"
        ],
        "summary": "Remove a number from the do-not-call list",
        "description": "Campaigns may dial the number again. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Entry id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No entry with that id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/privacy/erase": {
      "post": {
        "operationId": "eraseCaller",
        "tags": [
          "Privacy"
        ],
        "summary": "Erase a caller's data",
        "description": "Erases one caller's data in the key's organization: their calls lose transcript, summary, caller data and recording (the customer becomes \"[erased]\"; duration, cost, outcome and evaluation stay for billing), their returning-caller contact is deleted, campaign contacts with their number are erased, log entries and activity log entries naming them are redacted, stored webhook deliveries mentioning them deleted and Twilio-held recordings of their calls deleted; with `cancelBookings` the calendar bookings their calls made are cancelled. Do-not-call entries are kept. Calls still running are skipped. `dryRun` only counts. Idempotent: a second run finds nothing left. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EraseRequest"
              },
              "example": {
                "number": "+14155550100",
                "dryRun": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What was erased (or, with dryRun, would be).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EraseResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, neither a number nor a customer id, a number that is not one, or a malformed customer id (`fields`).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/campaigns": {
      "get": {
        "operationId": "listCampaigns",
        "tags": [
          "Campaigns"
        ],
        "summary": "List campaigns",
        "description": "Outbound campaigns and their progress. Contacts (personal data) are left out; contactCount says how many there are. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/campaigns/{id}/contacts": {
      "post": {
        "operationId": "addCampaignContacts",
        "tags": [
          "Campaigns"
        ],
        "summary": "Add contacts to a campaign",
        "description": "Appends contacts to a Paused or Scheduled campaign (the automation action \"add a contact to a campaign\"). Phones are cleaned like the console's; numbers the organization may not dial (dial policy), repeats and phones already on the campaign are skipped. A campaign holds at most 10,000 contacts. A Scheduled campaign dials them at its start time; start one now with POST /api/v1/campaigns/{id}/start. Test keys: 403 test_mode_phone. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Campaign id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignContactsAdd"
              },
              "example": {
                "contacts": [
                  {
                    "phone": "+14155550100",
                    "vars": {
                      "name": "Ada"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignContactsResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, `contacts` not an array, more than 10,000 contacts, or the campaign would hold more than 10,000.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No campaign with that id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The campaign is Running or Completed: contacts can only be added to a paused or scheduled campaign.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/campaigns/{id}/start": {
      "post": {
        "operationId": "startCampaign",
        "tags": [
          "Campaigns"
        ],
        "summary": "Start a campaign's outbound calls",
        "description": "Starts the dialer now with the console's checks: Twilio connected, credits and the monthly limit, the assistant and the caller ID; per contact the calling window (08:00-21:00 callee time) and the do-not-call list. Every dial spends credits. Test keys: 403 test_mode_phone. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Campaign id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignStartResult"
                }
              }
            }
          },
          "400": {
            "description": "Not ready: Twilio not connected, the assistant or the caller ID missing, or a scheduled campaign without a start time.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "No credits, or the monthly limit is reached.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No campaign with that id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Finished, changed while starting, its contacts removed by data retention, or the organization is not set up.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/hooks": {
      "get": {
        "operationId": "listHooks",
        "tags": [
          "Automation"
        ],
        "summary": "List hook subscriptions",
        "description": "Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookSubscriptionList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "subscribeHook",
        "tags": [
          "Automation"
        ],
        "summary": "Subscribe a URL to an event",
        "description": "REST hook subscribe (Zapier performSubscribe, Make, n8n). Events: call.ended, booking.created, voicemail.received, campaign.finished, lead.created, callback.requested. The same URL and event again answers the existing subscription (200). Deliveries carry x-voxa-signature like every webhook. At most 50 subscriptions per organization. Test keys: 403 test_mode_scope. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HookCreate"
              },
              "example": {
                "event": "call.ended",
                "url": "https://hooks.zapier.com/hooks/standard/123/abc/"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already subscribed: the existing subscription.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookSubscription"
                }
              }
            }
          },
          "201": {
            "description": "Subscribed.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookSubscription"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, an unknown event or field, or a URL that is not public https (`fields`).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The organization has 50 subscriptions already.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "unsubscribeHooks",
        "tags": [
          "Automation"
        ],
        "summary": "Unsubscribe by id or URL",
        "description": "Send `{ id }` or `{ url, event? }` as the JSON body, or as query parameters. Make and n8n call this when a scenario or workflow is turned off. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "A subscription id.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "url",
            "in": "query",
            "description": "Remove every subscription of this URL (optionally of one `event`).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "event",
            "in": "query",
            "description": "With `url`: only this event.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "call.ended",
                "booking.created",
                "voicemail.received",
                "campaign.finished",
                "lead.created",
                "callback.requested"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookDeleteResult"
                }
              }
            }
          },
          "400": {
            "description": "Neither id nor url, or an unknown event.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No subscription matched.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/hooks/{id}": {
      "get": {
        "operationId": "getHook",
        "tags": [
          "Automation"
        ],
        "summary": "Get a hook subscription",
        "description": "Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Subscription id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookSubscription"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No subscription with that id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deleteHook",
        "tags": [
          "Automation"
        ],
        "summary": "Unsubscribe one hook",
        "description": "Zapier's performUnsubscribe: call it with the id the subscribe answered. Needs the `write` scope.",
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "x-voxa-scope": "write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Subscription id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No subscription with that id.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/files": {
      "get": {
        "operationId": "listFiles",
        "tags": [
          "Knowledge"
        ],
        "summary": "List knowledge files",
        "description": "Metadata only; the content is not returned. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeFileList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/knowledge-bases": {
      "get": {
        "operationId": "listKnowledgeBases",
        "tags": [
          "Knowledge"
        ],
        "summary": "List knowledge bases",
        "description": "Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeBaseList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/numbers": {
      "get": {
        "operationId": "listNumbers",
        "tags": [
          "Numbers"
        ],
        "summary": "List phone numbers",
        "description": "Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/tools": {
      "get": {
        "operationId": "listTools",
        "tags": [
          "Tools"
        ],
        "summary": "List tools",
        "description": "Tools available to assistants. Request headers (often credentials) are never returned. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page, 1..100 (default 50).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The previous page's nextCursor. Opaque: pass it back unchanged, with the same filters. A cursor this API did not issue answers 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (limit out of range, or a cursor this API did not issue): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/analytics/summary": {
      "get": {
        "operationId": "getAnalyticsSummary",
        "tags": [
          "Analytics"
        ],
        "summary": "Call totals for a window",
        "description": "Counts, minutes, cost, outcomes, end reasons and per-assistant totals of the organization's calls started in [from, to). Test calls are left out (counted in testCalls). Calls still in progress count toward `calls` only; minutes, cost and the rates use finished calls. Reads at most 20,000 calls (newest first); past that `truncated` is true. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Start of the window: ISO-8601 or a unix timestamp. Default: 30 days before `to`.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "End of the window (exclusive). Default: now. At most 366 days after `from`.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assistantId",
            "in": "query",
            "description": "Only calls answered by this assistant.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string",
                      "description": "Start of the window.",
                      "format": "date-time"
                    },
                    "to": {
                      "type": "string",
                      "description": "End of the window (exclusive).",
                      "format": "date-time"
                    },
                    "assistantId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The assistant filter, if any."
                    },
                    "testCalls": {
                      "type": "integer",
                      "description": "Test calls in the window (left out of everything else)."
                    },
                    "calls": {
                      "type": "integer",
                      "description": "Calls started in the window, in progress included."
                    },
                    "finished": {
                      "type": "integer",
                      "description": "Calls that have ended."
                    },
                    "minutes": {
                      "type": "number",
                      "description": "Minutes of finished calls."
                    },
                    "avgDurationSec": {
                      "type": "integer",
                      "description": "Average duration of finished calls in seconds."
                    },
                    "cost": {
                      "type": "number",
                      "description": "Dollars charged for finished calls."
                    },
                    "successRate": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Percent of finished calls that did not fail; null without finished calls."
                    },
                    "status": {
                      "type": "object",
                      "properties": {
                        "live": {
                          "type": "integer"
                        },
                        "interrupted": {
                          "type": "integer"
                        },
                        "completed": {
                          "type": "integer"
                        },
                        "failed": {
                          "type": "integer"
                        },
                        "transferred": {
                          "type": "integer"
                        },
                        "voicemail": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "live",
                        "interrupted",
                        "completed",
                        "failed",
                        "transferred",
                        "voicemail"
                      ],
                      "description": "Calls by status."
                    },
                    "byType": {
                      "type": "object",
                      "properties": {
                        "Inbound": {
                          "type": "integer"
                        },
                        "Outbound": {
                          "type": "integer"
                        },
                        "Web": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "Inbound",
                        "Outbound",
                        "Web"
                      ],
                      "description": "Calls by type."
                    },
                    "evaluation": {
                      "type": "object",
                      "properties": {
                        "evaluated": {
                          "type": "integer"
                        },
                        "passed": {
                          "type": "integer"
                        },
                        "passRate": {
                          "type": [
                            "number",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "evaluated",
                        "passed",
                        "passRate"
                      ],
                      "description": "Success evaluations of finished calls."
                    },
                    "sentiment": {
                      "type": "object",
                      "properties": {
                        "scored": {
                          "type": "integer"
                        },
                        "avg": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "-1..1"
                        }
                      },
                      "required": [
                        "scored",
                        "avg"
                      ],
                      "description": "Caller sentiment of analysed calls."
                    },
                    "topReasons": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "reason": {
                            "type": "string"
                          },
                          "calls": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "reason",
                          "calls"
                        ]
                      },
                      "description": "The ten most common end reasons."
                    },
                    "assistants": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "calls": {
                            "type": "integer"
                          },
                          "minutes": {
                            "type": "number"
                          },
                          "cost": {
                            "type": "number"
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "calls",
                          "minutes",
                          "cost"
                        ]
                      },
                      "description": "Per-assistant totals, busiest first (at most 50)."
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "More than 20,000 calls: the totals cover the newest 20,000."
                    }
                  },
                  "required": [
                    "from",
                    "to",
                    "assistantId",
                    "testCalls",
                    "calls",
                    "finished",
                    "minutes",
                    "avgDurationSec",
                    "cost",
                    "successRate",
                    "status",
                    "byType",
                    "evaluation",
                    "sentiment",
                    "topReasons",
                    "assistants"
                  ],
                  "description": "Call totals of the organization for a window."
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter (from, to or assistantId): `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/logs": {
      "get": {
        "operationId": "listLogs",
        "tags": [
          "Logs"
        ],
        "summary": "List log entries",
        "description": "The organization's log entries (the console's Logs page), newest first. Contexts are redacted (secrets, phone numbers, card numbers, query strings); the person behind a console request is never named. Page back with `until` = the previous page's `nextUntil`; follow new entries with `since` (entries at the same instant may repeat: de-duplicate by id). Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "parameters": [
          {
            "name": "callId",
            "in": "query",
            "description": "Only entries about this call.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "Only entries at or after this time: ISO-8601 or a unix timestamp.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "until",
            "in": "query",
            "description": "Only entries strictly before this time.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "level",
            "in": "query",
            "description": "Comma list of debug, info, warn, error.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "description": "Comma list of api, webhook, event, error, metric, call.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Entries per page, 1..500 (default 100).",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "ts": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "level": {
                            "type": "string",
                            "enum": [
                              "debug",
                              "info",
                              "warn",
                              "error"
                            ]
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "api",
                              "webhook",
                              "event",
                              "error",
                              "metric",
                              "call"
                            ]
                          },
                          "message": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string",
                            "description": "Where the entry was written: console, voice, browser..."
                          },
                          "method": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string",
                            "description": "Request path, query string scrubbed."
                          },
                          "status": {
                            "type": "integer"
                          },
                          "durationMs": {
                            "type": "number"
                          },
                          "value": {
                            "type": "number",
                            "description": "Metric value."
                          },
                          "unit": {
                            "type": "string"
                          },
                          "context": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "Redacted structured details (callId, assistantId, ...)."
                          }
                        },
                        "required": [
                          "id",
                          "ts",
                          "level",
                          "kind",
                          "message",
                          "source"
                        ]
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextUntil": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `until` for the next (older) page."
                    }
                  },
                  "required": [
                    "data",
                    "count",
                    "hasMore",
                    "nextUntil"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter: `fields` names it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "The current key",
        "description": "The key making the request (with its effective scopes) and its organization. Handy for checking a key works. Needs the `read` scope.",
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "x-voxa-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "tags": [
          "Meta"
        ],
        "summary": "This OpenAPI document",
        "description": "Public, no key needed. Cached for five minutes.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "An OpenAPI 3.1 document."
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/voice/token": {
      "post": {
        "operationId": "createCallToken",
        "tags": [
          "Client (public key)"
        ],
        "summary": "Get a call token with a public key",
        "description": "What the widget and web SDK call from the browser: a call token for the voice server. Published assistants only; an inline `assistant` needs the organization's allowInlineAssistants switch. A Lite-mode `videoAgentId` adds the avatar (the answer carries `video`); Full-mode agents start through POST /api/video/sessions. Rate limited per public key and per address (60 per 10 minutes each). `customerId` is stored on the call and keys returning-caller memory only with a valid `customerIdSignature` from your server (HMAC-SHA256 of `${customerId}.${expires}` with the organization's customer id secret, sent as `<expires>.<hex>`); without one it is ignored and the answer says why in `customerIdIgnored`. A signed-in console session may call it without a public key (not covered here).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoiceTokenRequest"
              },
              "example": {
                "publicKey": "pk_...",
                "assistantId": "asst_7f3a91c2",
                "via": "sdk",
                "variableValues": {
                  "name": "Sam"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceToken"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (fields, e.g. a malformed customerId or customerIdSignature), both assistantId and assistant, or a Full-mode video agent (reason full_mode).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "401": {
            "description": "Unknown or missing public key (reason unknown_public_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "402": {
            "description": "The organization cannot make calls right now (reason no_credits / monthly_limit; test_budget with the test public key; test calls also need credits or the organization's own model provider key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "403": {
            "description": "origin_not_allowed, not_published, inline_not_allowed, foreign_assistant, not_squad_member or workflow_assistant_mismatch (see reason).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "404": {
            "description": "Unknown assistant, workflow, squad or video agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "409": {
            "description": "Video agents need a HeyGen key on the deployment (reason no_video_key), and never run on a HIPAA assistant without the operator-confirmed video BAA (reason video_hipaa).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from this address or for this key (reason rate_limited_ip / rate_limited_key); see Retry-After. Also the call caps: the organization's concurrent calls (reason concurrency_limit) or the assistant's daily budget (reason daily_budget).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/chat/messages": {
      "post": {
        "operationId": "sendChatMessage",
        "tags": [
          "Client (public key)"
        ],
        "summary": "Text chat with an assistant",
        "description": "What the widget's chat view (data-mode=\"chat\") and the web SDK's voxa.chat() call: start a chat with a Published assistant (assistantId) and send messages with the `session` each answer returns. The same assistant, knowledge and tools as on calls; each reply is billed per message plus model usage on Voxa's keys. The same public-key checks and limits as call tokens, plus per-chat message limits. A chat closes after 24 hours without messages (410 afterwards: start a new one).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatMessageRequest"
              },
              "example": {
                "publicKey": "pk_...",
                "assistantId": "asst_7f3a91c2",
                "text": "Do you open on Sundays?"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessages"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (fields: text, assistantId, customerId, customerIdSignature).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "401": {
            "description": "Unknown or missing public key (reason unknown_public_key). Also an unknown or expired session (reason invalid_session / session_expired).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "402": {
            "description": "The organization cannot pay for replies (reason no_credits / monthly_limit).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "403": {
            "description": "The request's origin is not in the organization's allowed origins (reason origin_not_allowed), or the record is not Published (reason not_published). Also foreign_assistant, test_key (chats run on the live key) or model_not_available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "404": {
            "description": "Unknown assistant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "409": {
            "description": "HIPAA assistants take no text chats (reason hipaa).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "410": {
            "description": "The chat has ended (reason conversation_closed): start a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from this address or for this key (reason rate_limited_ip / rate_limited_key); see Retry-After. Also rate_limited (messages per chat and organization), conversation_limit (50 messages per chat) and daily_budget.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The model failed (reason model_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "503": {
            "description": "No model key is available (reason no_model_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          }
        }
      }
    },
    "/api/video/agents/{id}": {
      "get": {
        "operationId": "getVideoAgent",
        "tags": [
          "Client (public key)"
        ],
        "summary": "Get a video agent's public facts",
        "description": "Which start route to take (mode) and the avatar to show.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Video agent id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "publicKey",
            "in": "query",
            "description": "The organization's public key (pk_...).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoAgentPublic"
                }
              }
            }
          },
          "401": {
            "description": "Unknown or missing public key (reason unknown_public_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "403": {
            "description": "The request's origin is not allowed (reason origin_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "404": {
            "description": "Unknown agent, another organization's, or a draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from this address or for this key (reason rate_limited_ip / rate_limited_key); see Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/video/sessions": {
      "post": {
        "operationId": "startVideoSession",
        "tags": [
          "Client (public key)"
        ],
        "summary": "Start a Full-mode video session",
        "description": "HeyGen runs speech recognition, the model and the voice. Start the session in the browser with HeyGen's web SDK, POST /alive every 30 seconds and /end when it is over. The call record is written In progress at this point.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoSessionRequest"
              },
              "example": {
                "publicKey": "pk_...",
                "videoAgentId": "vide_a88d176f",
                "via": "sdk"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FullVideoSession"
                }
              }
            }
          },
          "400": {
            "description": "Missing videoAgentId (fields) or a Lite-mode agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "401": {
            "description": "Unknown or missing public key (reason unknown_public_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "402": {
            "description": "The organization cannot pay (reason no_credits / monthly_limit): its credits must cover its live calls' running cost plus the first minute of this session; test_budget with the test public key. Sandbox agents are free and exempt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "403": {
            "description": "The request's origin is not in the organization's allowed origins (reason origin_not_allowed), or the record is not Published (reason not_published). With the test public key only sandbox agents start (reason test_mode_video).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "404": {
            "description": "Unknown agent, or another organization's.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "409": {
            "description": "Video agents are not enabled on the deployment (no_video_key), the agent's prompt or model is not registered at HeyGen (no_context, no_llm), or it names a HIPAA assistant without the operator-confirmed video BAA (video_hipaa).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from this address or for this key (reason rate_limited_ip / rate_limited_key); see Retry-After. Also the call caps: the organization's concurrent calls (reason concurrency_limit) or the assistant's daily budget (reason daily_budget).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "HeyGen failed (reason heygen; other HeyGen statuses pass through).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "503": {
            "description": "The deployment's HeyGen account cannot start sessions right now (reason video_unavailable).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          }
        }
      }
    },
    "/api/video/sessions/{callId}/alive": {
      "post": {
        "operationId": "videoSessionAlive",
        "tags": [
          "Client (public key)"
        ],
        "summary": "Heartbeat of a Full-mode video session",
        "description": "Every 30 seconds while the session runs; a call that stops beating for three minutes is closed as interrupted.",
        "security": [
          {
            "sessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "callId",
            "in": "path",
            "description": "The callId the start answered.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Noted."
          },
          "401": {
            "description": "Bad session auth, or auth for another call (reason invalid_session_auth).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "404": {
            "description": "No Full-mode video call with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "409": {
            "description": "The call is already over.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "410": {
            "description": "End the call: the session limit has passed (reason session_limit), or the organization can no longer pay for its live calls (reason no_credits / monthly_limit; the server has stopped the session and finished the call).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/video/sessions/{callId}/end": {
      "post": {
        "operationId": "endVideoSession",
        "tags": [
          "Client (public key)"
        ],
        "summary": "End a Full-mode video session",
        "description": "Stops the HeyGen session, imports its transcript (the body's lines are the fallback), charges the organization and finishes the call. Idempotent. The body is parsed as JSON whatever its content-type (navigator.sendBeacon sends text/plain).",
        "security": [
          {
            "sessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "callId",
            "in": "path",
            "description": "The callId the start answered.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoSessionEnd"
              },
              "example": {
                "reason": "user"
              }
            },
            "text/plain": {
              "schema": {
                "$ref": "#/components/schemas/VideoSessionEnd"
              },
              "example": {
                "reason": "user"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoSessionEnded"
                }
              }
            }
          },
          "400": {
            "description": "Malformed transcript (fields).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "401": {
            "description": "Bad session auth, or auth for another call (reason invalid_session_auth).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "404": {
            "description": "No Full-mode video call with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "webhooks": {
    "status-update": {
      "post": {
        "operationId": "webhookStatusUpdate",
        "tags": [
          "Webhooks"
        ],
        "summary": "A call connected",
        "description": "Sent when the voice server has the call live. Sent with x-voxa-event: status-update.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookStatusUpdate"
              },
              "example": {
                "event": "status-update",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "status": "in-progress",
                "type": "Inbound",
                "customer": "+1 415 555 0134"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "transcript": {
      "post": {
        "operationId": "webhookTranscript",
        "tags": [
          "Webhooks"
        ],
        "summary": "New transcript lines",
        "description": "Batches of lines during the call. Never sent for HIPAA assistants. Sent with x-voxa-event: transcript.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookTranscript"
              },
              "example": {
                "event": "transcript",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "lines": [
                  {
                    "who": "Caller",
                    "text": "I'd like to book a cleaning.",
                    "at": "2026-10-03T14:33:20.000Z"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "tool-calls": {
      "post": {
        "operationId": "webhookToolCalls",
        "tags": [
          "Webhooks"
        ],
        "summary": "The assistant called a tool",
        "description": "After a tool call finished. Not sent for HIPAA assistants or for the legacy book/order tools (those reach the server URL as the request itself). With PII redaction on, string values of the arguments and the result are masked like the transcript (the tool itself received the real values). Sent with x-voxa-event: tool-calls.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookToolCalls"
              },
              "example": {
                "event": "tool-calls",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "name": "check_availability",
                "arguments": {
                  "day": "Tuesday"
                },
                "result": "10:30 and 14:00 are free"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "end-of-call-report": {
      "post": {
        "operationId": "webhookEndOfCallReport",
        "tags": [
          "Webhooks"
        ],
        "summary": "A call ended and was saved",
        "description": "The call as GET /api/v1/calls/{id} returns it (customer redacted under HIPAA). For a Full-mode video call there is no assistantId; `videoAgentId` names the agent and the event goes to the organization's server URL. Sent with x-voxa-event: end-of-call-report.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndOfCallReport"
              },
              "example": {
                "event": "end-of-call-report",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "call": {
                  "id": "call_9a1",
                  "assistantId": "asst_7f3a91c2",
                  "asst": "Riley - Front desk",
                  "customer": "+1 415 555 0134",
                  "type": "Inbound",
                  "reason": "Customer ended call",
                  "kind": "ok",
                  "status": "completed",
                  "secs": 192,
                  "startedAt": "2026-10-03T14:33:10.000Z",
                  "costN": 0.21,
                  "summary": "Booked a cleaning for Tuesday 10:30.",
                  "data": [
                    {
                      "k": "appointment",
                      "v": "Tue 10:30"
                    }
                  ],
                  "evalPass": true,
                  "recordingUrl": "/api/v1/calls/call_9a1/recording",
                  "transcript": [
                    {
                      "who": "Assistant",
                      "text": "Hi, thanks for calling Acme Dental."
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "analysis": {
      "post": {
        "operationId": "webhookAnalysis",
        "tags": [
          "Webhooks"
        ],
        "summary": "The post-call analysis finished",
        "description": "After end-of-call-report. Full-mode video calls carry videoAgentId instead of assistantId. Sent with x-voxa-event: analysis.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookAnalysis"
              },
              "example": {
                "event": "analysis",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "summary": "Booked a cleaning for Tuesday 10:30.",
                "data": {
                  "appointment": "Tue 10:30",
                  "insurance": null
                },
                "evaluation": {
                  "type": "pass",
                  "pass": true
                },
                "analyzedAt": "2026-10-03T14:39:58.120Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "call.flagged": {
      "post": {
        "operationId": "webhookCallFlagged",
        "tags": [
          "Webhooks"
        ],
        "summary": "A call tripped a quality alert",
        "description": "After the post-call analysis (or, for a call no model analyzed, after its watched-word check): the total score was below the scorecard's threshold, the caller's sentiment was at or below its threshold, or a watched word was said. Once per call, at most 30 per organization an hour. HIPAA calls carry hipaa true and no sentiment, words or excerpts; excerpts of other calls are masked with the call's redaction. Sent with x-voxa-event: call.flagged.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCallFlagged"
              },
              "example": {
                "event": "call.flagged",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "reasons": [
                  "low_score",
                  "keyword"
                ],
                "score": 42,
                "keywords": [
                  "cancel"
                ],
                "excerpts": [
                  "Caller: I want to cancel my plan."
                ],
                "flaggedAt": "2026-10-03T14:40:02.000Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "billing.threshold": {
      "post": {
        "operationId": "webhookBillingThreshold",
        "tags": [
          "Webhooks"
        ],
        "summary": "A usage alert threshold was reached",
        "description": "This month's usage reached one of the organization's usage alert thresholds (once per month each). Sent to the organization's server URL only. Sent with x-voxa-event: billing.threshold.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookBillingThreshold"
              },
              "example": {
                "event": "billing.threshold",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "threshold": 80,
                "monthUsed": 80.4,
                "monthlyLimit": 100,
                "month": "2026-10"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "suite.finished": {
      "post": {
        "operationId": "webhookSuiteFinished",
        "tags": [
          "Webhooks"
        ],
        "summary": "A test suite run finished",
        "description": "Completed, or failed after at least one case; started from the console, by a publish of the suite's assistant, or through the API. Sent with x-voxa-event: suite.finished.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSuiteFinished"
              },
              "example": {
                "event": "suite.finished",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "suiteId": "suite_1",
                "name": "Front desk",
                "run": {
                  "id": "job_1",
                  "at": "2026-10-03T15:00:00.000Z",
                  "trigger": "manual",
                  "status": "done",
                  "passed": 4,
                  "failed": 1,
                  "total": 5,
                  "passRate": 0.8,
                  "regressed": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "voicemail.received": {
      "post": {
        "operationId": "webhookVoicemailReceived",
        "tags": [
          "Webhooks"
        ],
        "summary": "A caller left a voicemail",
        "description": "On a closed number (business hours with the voicemail after-hours rule). The transcript needs the organization's own Deepgram key and is masked with the number's assistant's PII redaction (a squad: its strictest member). HIPAA (any squad member): no recording, no transcript, the caller \"[redacted]\" and hipaa true. Sent with x-voxa-event: voicemail.received.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookVoicemailReceived"
              },
              "example": {
                "event": "voicemail.received",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_vm1",
                "from": "+14155550134",
                "to": "+14155550100",
                "durationSec": 21,
                "transcript": "Hi, please call me back.",
                "recordingUrl": "/api/v1/calls/call_vm1/recording"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "callback.requested": {
      "post": {
        "operationId": "webhookCallbackRequested",
        "tags": [
          "Webhooks"
        ],
        "summary": "A caller asked to be called back",
        "description": "The assistant scheduled a callback (its handoff settings allow callbacks), for example after a transfer nobody took. One open callback per number: a second request updates it. The preferred time is inside the team's hours (when set) and 08:00 to 21:00 in the caller's time. HIPAA calls: the number \"[redacted]\", no reason, hipaa true. Also sent to automation hooks subscribed to callback.requested. Sent with x-voxa-event: callback.requested.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCallbackRequested"
              },
              "example": {
                "event": "callback.requested",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callback": {
                  "id": "cb_1",
                  "callId": "call_1",
                  "number": "+14155550134",
                  "name": "Sam",
                  "reason": "Wants a quote for a new roof",
                  "preferredAt": "2026-10-16T15:00:00.000Z",
                  "timeZone": "America/Los_Angeles",
                  "status": "open",
                  "source": "assistant"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "report.ready": {
      "post": {
        "operationId": "webhookReportReady",
        "tags": [
          "Webhooks"
        ],
        "summary": "A scheduled report was built",
        "description": "A daily or weekly usage report (organization settings, Reports) was built, with the webhook option on. Sent to the organization's server URL only. The full report is read from the console (GET /api/reports/{id}). Sent with x-voxa-event: report.ready.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookReportReady"
              },
              "example": {
                "event": "report.ready",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "reportId": "rep_3f9a1c2b7d4e5a60",
                "period": {
                  "from": "2026-10-05T07:00:00.000Z",
                  "to": "2026-10-12T07:00:00.000Z",
                  "timeZone": "America/Los_Angeles",
                  "kind": "weekly",
                  "label": "Oct 5 – Oct 11, 2026"
                },
                "summary": {
                  "calls": 412,
                  "minutes": 1290.5,
                  "cost": 141.96,
                  "passRate": 0.87
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "payment.succeeded": {
      "post": {
        "operationId": "webhookPaymentSucceeded",
        "tags": [
          "Webhooks"
        ],
        "summary": "A payment during a call was paid",
        "description": "A payment link the assistant sent (collect_payment, your own Stripe account; from your Stripe webhook) or a keypad card entry (collect_card_by_keypad, Twilio <Pay>) was paid. Carries no card detail, link or caller number. Sent with x-voxa-event: payment.succeeded.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPaymentSucceeded"
              },
              "example": {
                "event": "payment.succeeded",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "payment": {
                  "id": "pay_0123456789abcdef",
                  "amount": 4500,
                  "currency": "usd",
                  "status": "paid",
                  "method": "link",
                  "description": "Deposit",
                  "confirmation": "pi_3Pxyz",
                  "at": "2026-10-16T14:35:02.000Z"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "payment.failed": {
      "post": {
        "operationId": "webhookPaymentFailed",
        "tags": [
          "Webhooks"
        ],
        "summary": "A payment during a call failed or expired",
        "description": "A payment link expired unpaid or its payment failed, or keypad card entry ended without a payment (`reason` says why). Sent with x-voxa-event: payment.failed.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPaymentFailed"
              },
              "example": {
                "event": "payment.failed",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "payment": {
                  "id": "pay_0123456789abcdef",
                  "amount": 4500,
                  "currency": "usd",
                  "status": "expired",
                  "method": "link",
                  "reason": "expired",
                  "at": "2026-10-16T15:05:02.000Z"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "sms.text_back": {
      "post": {
        "operationId": "webhookSmsTextBack",
        "tags": [
          "Webhooks"
        ],
        "summary": "A missed caller was texted back",
        "description": "A phone number with missed-call text-back on texted a caller nobody answered (hung up before the agent answered, not answered, or closed after hours), from the number itself through your own Twilio account; also sent when the send failed. Never for HIPAA numbers, withheld callers, numbers on the do-not-call list, during quiet hours (21:00 to 08:00 caller time) or twice to the same caller within 24 hours. Sent with x-voxa-event: sms.text_back.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSmsTextBack"
              },
              "example": {
                "event": "sms.text_back",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9b2",
                "numberId": "num_1",
                "from": "+14155550100",
                "to": "+14155550134",
                "trigger": "abandoned",
                "status": "sent",
                "sid": "SM0123456789abcdef0123456789abcdef",
                "sentAt": "2026-10-03T14:35:02.000Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "conversation.started": {
      "post": {
        "operationId": "webhookConversationStarted",
        "tags": [
          "Webhooks"
        ],
        "summary": "A text conversation started",
        "description": "Someone texted one of your numbers (SMS or WhatsApp, the number's Text messages settings) or wrote in the website chat, and the assistant opened a conversation. Never for HIPAA assistants (they take no text channels). Chat visitors without a signed customer id have participant \"\". Sent with x-voxa-event: conversation.started.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConversationStarted"
              },
              "example": {
                "event": "conversation.started",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "conversationId": "conv_3f9a1c2b7d4e5a60",
                "channel": "sms",
                "participant": "+14155550134",
                "numberId": "num_1",
                "startedAt": "2026-10-16T14:31:00.000Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "conversation.ended": {
      "post": {
        "operationId": "webhookConversationEnded",
        "tags": [
          "Webhooks"
        ],
        "summary": "A text conversation ended",
        "description": "The conversation closed: idle for the configured hours (24 by default), the person replied STOP, it reached its message limit, or someone closed it in the console. Carries the analysis when one ran (summary, data, evaluation; same prompt and schema as calls, metered like call analysis). Sent with x-voxa-event: conversation.ended.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConversationEnded"
              },
              "example": {
                "event": "conversation.ended",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "conversationId": "conv_3f9a1c2b7d4e5a60",
                "channel": "sms",
                "participant": "+14155550134",
                "numberId": "num_1",
                "startedAt": "2026-10-16T14:31:00.000Z",
                "closedAt": "2026-10-17T14:40:00.000Z",
                "reason": "idle",
                "messages": 6,
                "summary": "Asked about opening hours and booked a cleaning.",
                "data": {},
                "evaluation": {
                  "type": "pass",
                  "pass": true
                },
                "cost": 0.0213
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "call.ended": {
      "post": {
        "operationId": "webhookCallEnded",
        "tags": [
          "Webhooks"
        ],
        "summary": "A call ended (automation hook)",
        "description": "Sent only to URLs subscribed with POST /api/v1/hooks (never to the server URLs), with the flat fields an automation maps; the full report is end-of-call-report. Not for test calls. HIPAA calls: customer \"[redacted]\". Sent with x-voxa-event: call.ended.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCallEnded"
              },
              "example": {
                "event": "call.ended",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "assistantName": "Riley",
                "type": "Inbound",
                "customer": "+1 415 555 0134",
                "status": "completed",
                "endedReason": "Customer ended call",
                "startedAt": "2026-10-03T14:31:00.000Z",
                "durationSeconds": 184,
                "summary": "Booked a cleaning for Thursday.",
                "recordingUrl": "/api/v1/calls/call_9a1/recording",
                "consoleUrl": "https://console.example.com/calls?c=call_9a1"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "booking.created": {
      "post": {
        "operationId": "webhookBookingCreated",
        "tags": [
          "Webhooks"
        ],
        "summary": "An appointment was booked (automation hook)",
        "description": "The assistant booked into a connected calendar during a call. Subscribed hooks only; not for test calls. HIPAA calls: the title is \"Appointment\". Sent with x-voxa-event: booking.created.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookBookingCreated"
              },
              "example": {
                "event": "booking.created",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "booking": {
                  "provider": "google_calendar",
                  "integrationId": "int_1a2b",
                  "eventId": "voxa1f2e",
                  "start": "2026-10-09T13:00:00.000Z",
                  "end": "2026-10-09T13:30:00.000Z",
                  "title": "Appointment with Acme Dental",
                  "timeZone": "America/New_York"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "campaign.finished": {
      "post": {
        "operationId": "webhookCampaignFinished",
        "tags": [
          "Webhooks"
        ],
        "summary": "A campaign finished (automation hook)",
        "description": "Every contact settled. Counts by outcome only, never the numbers. Subscribed hooks only. Sent with x-voxa-event: campaign.finished.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCampaignFinished"
              },
              "example": {
                "event": "campaign.finished",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "campaignId": "camp_3c1",
                "name": "October reminders",
                "total": 120,
                "dialed": 118,
                "outcomes": {
                  "completed": 97,
                  "no-answer": 15,
                  "voicemail": 6,
                  "dnc": 2
                },
                "finishedAt": "2026-10-03T18:00:00.000Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    },
    "lead.created": {
      "post": {
        "operationId": "webhookLeadCreated",
        "tags": [
          "Webhooks"
        ],
        "summary": "A lead was created in the CRM (automation hook)",
        "description": "The CRM automation (after a call's analysis) or the assistant's create_crm_lead tool created a lead. The record's id only: read the person from your CRM. Subscribed hooks only. Sent with x-voxa-event: lead.created.",
        "parameters": [
          {
            "name": "x-voxa-event",
            "in": "header",
            "required": true,
            "description": "The event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in seconds when the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-voxa-signature",
            "in": "header",
            "required": false,
            "description": "`sha256=` + hex HMAC-SHA256 of `${timestamp}.${rawBody}` keyed with the organization's webhook secret (whsec_...). Absent when no secret is configured.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookLeadCreated"
              },
              "example": {
                "event": "lead.created",
                "timestamp": "2026-10-03T14:40:00.000Z",
                "assistantId": "asst_7f3a91c2",
                "callId": "call_9a1",
                "provider": "hubspot",
                "recordId": "5101",
                "recordType": "contact",
                "via": "post-call"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges. 3xx (redirects are not followed) and 4xx stop the retries; 5xx, timeouts (8 s) and network errors are retried, 3 attempts in all (after 1 s and 3 s)."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong, for people."
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Per-field messages for validation errors, keyed by field name (nested settings as `parent.child`)."
          },
          "reason": {
            "type": "string",
            "description": "Machine-readable cause on some refusals (e.g. ip_not_allowed, test_mode_phone, test_mode_scope, test_budget). test_mode_scope: test keys cannot DELETE, erase personal data or change the do-not-call list."
          }
        },
        "required": [
          "error"
        ],
        "description": "Every /api/v1 error."
      },
      "PublicError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong, for people."
          },
          "reason": {
            "type": "string",
            "description": "Machine-readable cause: rate_limited_ip, rate_limited_key, unknown_public_key, origin_not_allowed, not_published, inline_not_allowed, foreign_assistant, not_squad_member, workflow_assistant_mismatch, no_credits, monthly_limit, no_video_key, video_hipaa, no_context, no_llm, full_mode, invalid_session_auth..."
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Per-field messages for validation errors."
          }
        },
        "required": [
          "error"
        ],
        "description": "Errors of the public-key (client) routes: `{ error, reason?, fields? }` plus, for some reasons, extra facts (e.g. `member` for a squad member that is not published)."
      },
      "Assistant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "example": "asst_7f3a91c2"
          },
          "orgId": {
            "type": "string",
            "description": "Owning organization.",
            "readOnly": true
          },
          "name": {
            "type": "string",
            "description": "Up to 80 characters. Required on create.",
            "maxLength": 80
          },
          "color": {
            "type": "integer",
            "description": "Avatar color index.",
            "minimum": 0,
            "maximum": 3
          },
          "status": {
            "type": "string",
            "enum": [
              "Published",
              "Draft"
            ],
            "description": "New assistants start as Draft. Draft → Published saves the configuration as a new version."
          },
          "provider": {
            "type": "string",
            "enum": [
              "OpenAI",
              "Anthropic",
              "Google"
            ],
            "description": "Model provider."
          },
          "model": {
            "type": "string",
            "description": "Model id, e.g. gpt-4o. On Voxa's model keys only the priced models are accepted (gpt-4o, gpt-4o-mini, gpt-4.1, claude-sonnet-4, claude-haiku-4, gemini-2.5-flash, gemini-2.5-pro and a few more; a 400 on `model` lists them); with your own key for the provider any model id.",
            "maxLength": 80
          },
          "firstMessage": {
            "type": "string",
            "description": "What the assistant says first.",
            "maxLength": 2000
          },
          "prompt": {
            "type": "string",
            "description": "System prompt.",
            "maxLength": 20000
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2
          },
          "maxTokens": {
            "type": "integer",
            "description": "Stored; not applied to live calls.",
            "minimum": 1,
            "maximum": 32000
          },
          "voiceProvider": {
            "type": "string",
            "enum": [
              "Cartesia",
              "ElevenLabs",
              "Deepgram",
              "OpenAI"
            ]
          },
          "voice": {
            "type": "string",
            "description": "Voice name.",
            "maxLength": 80
          },
          "voiceId": {
            "type": "string",
            "description": "Provider voice id used instead of the mapped one (non-Deepgram voices).",
            "maxLength": 200
          },
          "speed": {
            "type": "number",
            "description": "Stored; not applied to live calls.",
            "minimum": 0.5,
            "maximum": 2
          },
          "denoise": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "backchannel": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "tProvider": {
            "type": "string",
            "enum": [
              "Deepgram",
              "AssemblyAI",
              "Google",
              "OpenAI"
            ],
            "description": "Transcriber provider."
          },
          "tModel": {
            "type": "string",
            "description": "Transcriber model. Deepgram: nova-3, nova-3-multi, flux-general-en, flux-general-multi or nova-2; a create without it gets the deployment's default (nova-3 unless configured); calls fall back to a model that covers the language.",
            "maxLength": 80
          },
          "sttEndOfTurn": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "threshold": {
                "type": "number",
                "description": "End-of-turn confidence (Deepgram's default 0.7): higher waits for clearer pauses, lower answers sooner.",
                "minimum": 0.5,
                "maximum": 1
              },
              "eager": {
                "type": "number",
                "description": "Eager end-of-turn confidence: the agent starts preparing a reply early. At most `threshold` (0.7 when unset); absent = off.",
                "minimum": 0.3,
                "maximum": 0.9
              },
              "timeoutMs": {
                "type": "integer",
                "description": "End the turn after this much silence, in milliseconds (default 5000).",
                "minimum": 500,
                "maximum": 10000
              }
            },
            "description": "Flux end-of-turn detection (tModel flux-general-en or flux-general-multi; other models ignore it). Absent fields use the defaults; null or {} clears. Errors name the setting (`sttEndOfTurn.eager`).",
            "additionalProperties": false
          },
          "sttKeyterms": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Keyterm prompting (Nova-3 and Flux): product names, jargon and proper nouns to recognise, one word or phrase per item. Trimmed and de-duplicated; no control characters; null clears (stored as []).",
            "maxItems": 100
          },
          "language": {
            "type": "string",
            "description": "BCP-47 language, e.g. en-US.",
            "maxLength": 20
          },
          "languages": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "primary": {
                "type": "string",
                "enum": [
                  "en",
                  "es",
                  "fr",
                  "de",
                  "it",
                  "pt",
                  "nl",
                  "hi",
                  "ru",
                  "ja"
                ],
                "description": "The language calls start in unless the caller's language is known up front."
              },
              "supported": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Languages the call may switch to mid-call when the caller speaks them (always includes primary). More than one makes calls use the multilingual transcriber.",
                "maxItems": 10
              },
              "greetings": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 2000
                },
                "description": "First message per language code, used when the call starts in that language."
              },
              "voices": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 200
                },
                "description": "Voice per language code: a voice name (Maya) or a provider voice id of the assistant's voice provider, a cloned voice's id included."
              }
            },
            "required": [
              "primary"
            ],
            "description": "Multilingual calls: detect the caller's language and switch voice and replies mid-call. null clears. Errors name the setting (`languages.greetings.es`).",
            "additionalProperties": false
          },
          "endpoint": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "recording": {
            "type": "boolean",
            "description": "Record calls."
          },
          "recordingNotice": {
            "type": "boolean",
            "description": "Say a recording notice before the first message of recorded calls (default on)."
          },
          "recordingNoticeText": {
            "type": "string",
            "description": "The notice's wording; empty = \"This call may be recorded.\"",
            "maxLength": 300
          },
          "hipaa": {
            "type": "boolean",
            "description": "No transcript, recording or analysis leaves the call."
          },
          "waitSeconds": {
            "type": "number",
            "description": "Stored; not applied to live calls.",
            "minimum": 0,
            "maximum": 5
          },
          "interruptWords": {
            "type": "integer",
            "description": "Stored; not applied to live calls.",
            "minimum": 0,
            "maximum": 20
          },
          "silenceTimeout": {
            "type": "integer",
            "description": "Seconds of silence before hanging up.",
            "minimum": 1,
            "maximum": 600
          },
          "maxDuration": {
            "type": "integer",
            "description": "Maximum call length in minutes.",
            "minimum": 1,
            "maximum": 180
          },
          "voicemail": {
            "type": "boolean",
            "description": "Leave a voicemail on outbound calls that reach one."
          },
          "voicemailMessage": {
            "type": "string",
            "maxLength": 1000
          },
          "endPhrases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Phrases that end the call.",
            "maxItems": 50
          },
          "toolIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tool records the assistant may call.",
            "maxItems": 50
          },
          "serverUrl": {
            "type": "string",
            "description": "Receives webhooks and tool calls for this assistant.",
            "maxLength": 500
          },
          "transferNumber": {
            "type": "string",
            "description": "E.164 destination of the built-in transfer tool. A new value must pass the dial policy (no premium-rate prefixes; a country the organization may dial), else 400 on the field.",
            "maxLength": 20
          },
          "handoff": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "cold",
                  "warm"
                ],
                "description": "warm: the person hears a short summary first and presses 1 to take the call."
              },
              "team": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "members": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "number": {
                          "type": "string",
                          "description": "E.164; must pass the dial policy."
                        },
                        "name": {
                          "type": "string",
                          "maxLength": 60
                        }
                      },
                      "required": [
                        "number"
                      ]
                    },
                    "description": "1 to 10 people.",
                    "maxItems": 10
                  },
                  "ring": {
                    "type": "string",
                    "enum": [
                      "sequential",
                      "simultaneous"
                    ],
                    "description": "One after another in list order, or all at once (at most 5; the first to accept wins)."
                  },
                  "ringSecs": {
                    "type": "integer",
                    "description": "Seconds each ring lasts, 10 to 60 (default 20).",
                    "minimum": 10,
                    "maximum": 60
                  },
                  "holdMusicUrl": {
                    "type": "string",
                    "description": "https link to an .mp3 or .wav file played to the waiting caller; absent = Twilio's hold music."
                  },
                  "hours": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "window": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "{ start: \"HH:MM\", end: \"HH:MM\", days: [0..6] }"
                      },
                      "timeZone": {
                        "type": "string",
                        "description": "IANA zone."
                      }
                    },
                    "required": [
                      "window",
                      "timeZone"
                    ],
                    "description": "Outside these hours the assistant offers a callback instead."
                  }
                },
                "required": [
                  "members"
                ],
                "description": "Ring a team instead of transferNumber."
              },
              "fallback": {
                "type": "string",
                "enum": [
                  "assistant",
                  "callback"
                ],
                "description": "Nobody accepts: the caller goes back to the assistant, which (callback, the default) offers a callback."
              },
              "callbacks": {
                "type": "boolean",
                "description": "The assistant may schedule callbacks (request_callback; callback.requested webhook)."
              },
              "staffJoinLine": {
                "type": "string",
                "description": "Said to the caller when staff barge in from the console.",
                "maxLength": 200
              }
            },
            "description": "Human handoff desk (warm transfers, a team, callbacks); null = off.",
            "additionalProperties": false
          },
          "bookingIntegrationId": {
            "type": "string",
            "description": "Calendar integration the booking tool books into; absent = the organization's first connected one.",
            "maxLength": 64
          },
          "dailyBudget": {
            "type": "number",
            "description": "Dollars this assistant may spend per UTC day; 0 or absent = no budget.",
            "minimum": 0,
            "maximum": 100000
          },
          "verifyMessage": {
            "type": "string",
            "description": "Text of the caller verification SMS (tools.verify). Must contain {{code}}; empty or absent = \"Your verification code is {{code}}. It expires in 5 minutes.\"",
            "maxLength": 320
          },
          "latencyTargetMs": {
            "type": "integer",
            "description": "Latency the caller should hear at most, in milliseconds: 0 (no target) or 300 to 10000. The console flags the assistant and calls whose measured median turn latency (latencyStats.p50) is above it.",
            "minimum": 0,
            "maximum": 10000
          },
          "engine": {
            "type": "string",
            "enum": [
              "pipeline",
              "openai-realtime",
              "gemini-live"
            ],
            "description": "How calls run: pipeline (transcriber, model and voice; the default) or a speech-to-speech model (openai-realtime, gemini-live) configured by `realtime`. Realtime engines refuse HIPAA mode and cannot be in a squad or run a workflow (400 on `engine`)."
          },
          "realtime": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "model": {
                "type": "string",
                "description": "Realtime model: gpt-realtime, gpt-realtime-mini, gpt-4o-mini-realtime-preview (openai-realtime); gemini-2.5-flash-native-audio-preview-09-2025, gemini-live-2.5-flash-preview (gemini-live). Others need the organization's own OpenAI / Google key.",
                "maxLength": 80
              },
              "voice": {
                "type": "string",
                "description": "A voice of the engine: marin, cedar, alloy, ash, ballad, coral, echo, sage, shimmer, verse (OpenAI); Puck, Charon, Kore, Fenrir, Aoede, Leda, Orus, Zephyr (Gemini).",
                "maxLength": 40
              },
              "vad": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "server",
                      "semantic"
                    ],
                    "description": "OpenAI: pause based (server, default) or semantic turn detection."
                  },
                  "silenceMs": {
                    "type": "integer",
                    "description": "Pause that ends the caller's turn, ms.",
                    "minimum": 200,
                    "maximum": 3000
                  },
                  "threshold": {
                    "type": "number",
                    "description": "OpenAI pause based: speech threshold (default 0.5).",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "eagerness": {
                    "type": "string",
                    "enum": [
                      "low",
                      "medium",
                      "high",
                      "auto"
                    ],
                    "description": "OpenAI semantic: how eagerly the model answers."
                  }
                },
                "description": "Turn detection; absent fields use the engine's defaults.",
                "additionalProperties": false
              },
              "fallback": {
                "type": "string",
                "enum": [
                  "pipeline",
                  "end"
                ],
                "description": "The engine cannot connect at call start (5 s): run the call on the pipeline (default) or end it."
              }
            },
            "required": [
              "model",
              "voice"
            ],
            "description": "Speech-to-speech settings (required with a realtime engine); null clears. Errors name the setting (`realtime.voice`).",
            "additionalProperties": false
          },
          "kb": {
            "type": "string",
            "description": "Knowledge base name.",
            "maxLength": 120
          },
          "summaryPrompt": {
            "type": "string",
            "description": "Post-call summary instructions.",
            "maxLength": 2000
          },
          "evaluation": {
            "type": "string",
            "enum": [
              "pass",
              "numeric",
              "descriptive"
            ],
            "description": "Success evaluation type of the post-call analysis."
          },
          "analysisSchema": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Field name in the extracted data."
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "number",
                    "boolean",
                    "enum"
                  ]
                },
                "description": {
                  "type": "string",
                  "description": "What the model should extract."
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Allowed values when `type` is enum."
                }
              },
              "required": [
                "key",
                "type"
              ],
              "description": "One structured-data field of the post-call analysis.",
              "additionalProperties": false
            },
            "description": "Structured data the post-call analysis extracts.",
            "maxItems": 50
          },
          "followUpSms": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "template": {
                "type": "string"
              },
              "when": {
                "type": "string",
                "enum": [
                  "always",
                  "success",
                  "data"
                ]
              },
              "requireField": {
                "type": "string"
              },
              "consent": {
                "type": "string",
                "enum": [
                  "implied",
                  "asked"
                ]
              }
            },
            "required": [
              "enabled",
              "template",
              "when",
              "consent"
            ],
            "description": "Follow-up SMS settings."
          },
          "payments": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "Adds collect_payment: a Stripe Checkout link from your own Stripe account (Provider keys → Payments during calls), texted to phone callers after their explicit yes or shown to web callers."
              },
              "maxAmount": {
                "type": "number",
                "description": "Largest amount per request in the first currency's main unit (two decimals at most).",
                "minimum": 0.01,
                "maximum": 100000
              },
              "currencies": {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^[a-z]{3}$"
                },
                "description": "Lower-case ISO 4217 codes (usd, eur, gbp, ...); the first is the default.",
                "minItems": 1,
                "maxItems": 10
              },
              "allowedDescriptions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "maxLength": 100
                },
                "description": "When set, a request's description must be one of these.",
                "maxItems": 20
              },
              "keypad": {
                "type": "boolean",
                "description": "Adds collect_card_by_keypad on phone calls through your own Twilio (Twilio <Pay> with your Pay connector)."
              }
            },
            "required": [
              "enabled",
              "maxAmount",
              "currencies"
            ],
            "description": "A compliance setting (admins; test keys may not change it). Never used on HIPAA calls; at most 2 payment requests per call.",
            "additionalProperties": false
          },
          "redaction": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "types": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "card",
                    "email",
                    "phone",
                    "address",
                    "ssn",
                    "iban",
                    "dob"
                  ]
                },
                "description": "What to mask; at least one (or the model pass) when enabled."
              },
              "model": {
                "type": "boolean",
                "description": "Also run a model pass for names and addresses the patterns missed."
              }
            },
            "required": [
              "enabled",
              "types"
            ],
            "description": "Mask personal data in what is stored and sent about calls.",
            "additionalProperties": false
          },
          "widget": {
            "type": "object",
            "properties": {
              "position": {
                "type": "string",
                "enum": [
                  "right",
                  "left"
                ],
                "description": "Corner of the floating button; default right."
              },
              "theme": {
                "type": "string",
                "enum": [
                  "light",
                  "dark"
                ],
                "description": "Default light."
              },
              "label": {
                "type": "string",
                "description": "Panel title (and button text unless buttonLabel is set); default Talk to us.",
                "maxLength": 60
              },
              "accent": {
                "type": "string",
                "description": "Accent color #rgb or #rrggbb (stored as lowercase #rrggbb): the button, focus ring and primary buttons; the text on it is white or near-black by WCAG contrast.",
                "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
              },
              "logo": {
                "type": "string",
                "description": "Header logo: an https URL (at most 2048 characters) or a data:image/png, jpeg or svg+xml base64 URL of at most 50 kB.",
                "maxLength": 51200
              },
              "welcome": {
                "type": "string",
                "description": "Plain text shown in the panel before the call starts.",
                "maxLength": 140
              },
              "buttonIcon": {
                "type": "string",
                "description": "mic (default), chat, phone, wave, or an https image URL."
              },
              "buttonLabel": {
                "type": "string",
                "description": "Button text next to the icon; default: the label.",
                "maxLength": 24
              },
              "poweredBy": {
                "type": "boolean",
                "description": "false hides the Powered by Voxa footer; default true."
              }
            },
            "description": "Embeddable widget appearance (the data-* attributes of /voxa-widget.js). Replaced as a whole on writes; defaults are left out; null or {} resets it.",
            "additionalProperties": false
          },
          "memory": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "Remember callers: the caller's contact is added to the prompt at call start (never under HIPAA)."
              },
              "template": {
                "type": "string",
                "description": "What the assistant is told, at most 600 characters; placeholders {{name}}, {{lastSeen}}, {{lastSummary}}, {{data}}, {{openItems}}, {{calls}}, {{data.<key>}}. Absent = the default template. The rendered text is cut to 800 characters.",
                "maxLength": 600
              }
            },
            "required": [
              "enabled"
            ],
            "description": "Returning-caller memory. Contacts are keyed by the caller's number (phone calls) or the web call's customerId.",
            "additionalProperties": false
          },
          "tools": {
            "type": "object",
            "properties": {
              "book": {
                "type": "boolean"
              },
              "transfer": {
                "type": "boolean"
              },
              "end": {
                "type": "boolean"
              },
              "order": {
                "type": "boolean"
              },
              "query": {
                "type": "boolean"
              },
              "dnc": {
                "type": "boolean"
              },
              "verify": {
                "type": "boolean"
              },
              "crm": {
                "type": "boolean"
              },
              "square": {
                "type": "boolean"
              },
              "jobber": {
                "type": "boolean"
              },
              "nexhealth": {
                "type": "boolean"
              }
            },
            "description": "Built-in tool switches. On writes a partial object is merged with the current value. `dnc` (add the caller to the do-not-call list) and `verify` (verify the caller by an SMS code through the organization's own Twilio) may be missing on older assistants (off).",
            "additionalProperties": false
          },
          "cost": {
            "type": "string",
            "description": "Estimated price per minute, recomputed from model, voiceProvider and tProvider on every write.",
            "readOnly": true,
            "example": "$0.11"
          },
          "latency": {
            "type": "string",
            "description": "Estimated end-to-end latency label.",
            "readOnly": true,
            "example": "~780 ms"
          },
          "lat": {
            "type": "number",
            "description": "Estimated end-to-end latency in milliseconds.",
            "readOnly": true
          },
          "pricingWarning": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set on save when the model, voice and transcriber setup would sell below Voxa's cost target on its own keys (components on your own provider keys exempt); null when fine. A warning only: the write succeeds and the same text comes back in the x-voxa-notice response header.",
            "readOnly": true
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "color",
          "status",
          "provider",
          "model",
          "firstMessage",
          "prompt",
          "temperature",
          "maxTokens",
          "voiceProvider",
          "voice",
          "speed",
          "denoise",
          "backchannel",
          "tProvider",
          "tModel",
          "language",
          "endpoint",
          "recording",
          "hipaa",
          "waitSeconds",
          "interruptWords",
          "silenceTimeout",
          "maxDuration",
          "voicemail",
          "voicemailMessage",
          "endPhrases",
          "serverUrl",
          "kb",
          "summaryPrompt",
          "evaluation",
          "tools",
          "cost",
          "latency",
          "lat",
          "updatedAt"
        ],
        "description": "A voice assistant: model, prompt, voice, transcriber, call rules, tools and analysis."
      },
      "AssistantCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Up to 80 characters. Required on create.",
            "maxLength": 80
          },
          "color": {
            "type": "integer",
            "description": "Avatar color index.",
            "minimum": 0,
            "maximum": 3
          },
          "status": {
            "type": "string",
            "enum": [
              "Published",
              "Draft"
            ],
            "description": "New assistants start as Draft. Draft → Published saves the configuration as a new version."
          },
          "provider": {
            "type": "string",
            "enum": [
              "OpenAI",
              "Anthropic",
              "Google"
            ],
            "description": "Model provider."
          },
          "model": {
            "type": "string",
            "description": "Model id, e.g. gpt-4o. On Voxa's model keys only the priced models are accepted (gpt-4o, gpt-4o-mini, gpt-4.1, claude-sonnet-4, claude-haiku-4, gemini-2.5-flash, gemini-2.5-pro and a few more; a 400 on `model` lists them); with your own key for the provider any model id.",
            "maxLength": 80
          },
          "firstMessage": {
            "type": "string",
            "description": "What the assistant says first.",
            "maxLength": 2000
          },
          "prompt": {
            "type": "string",
            "description": "System prompt.",
            "maxLength": 20000
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2
          },
          "maxTokens": {
            "type": "integer",
            "description": "Stored; not applied to live calls.",
            "minimum": 1,
            "maximum": 32000
          },
          "voiceProvider": {
            "type": "string",
            "enum": [
              "Cartesia",
              "ElevenLabs",
              "Deepgram",
              "OpenAI"
            ]
          },
          "voice": {
            "type": "string",
            "description": "Voice name.",
            "maxLength": 80
          },
          "voiceId": {
            "type": "string",
            "description": "Provider voice id used instead of the mapped one (non-Deepgram voices).",
            "maxLength": 200
          },
          "speed": {
            "type": "number",
            "description": "Stored; not applied to live calls.",
            "minimum": 0.5,
            "maximum": 2
          },
          "denoise": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "backchannel": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "tProvider": {
            "type": "string",
            "enum": [
              "Deepgram",
              "AssemblyAI",
              "Google",
              "OpenAI"
            ],
            "description": "Transcriber provider."
          },
          "tModel": {
            "type": "string",
            "description": "Transcriber model. Deepgram: nova-3, nova-3-multi, flux-general-en, flux-general-multi or nova-2; a create without it gets the deployment's default (nova-3 unless configured); calls fall back to a model that covers the language.",
            "maxLength": 80
          },
          "sttEndOfTurn": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "threshold": {
                "type": "number",
                "description": "End-of-turn confidence (Deepgram's default 0.7): higher waits for clearer pauses, lower answers sooner.",
                "minimum": 0.5,
                "maximum": 1
              },
              "eager": {
                "type": "number",
                "description": "Eager end-of-turn confidence: the agent starts preparing a reply early. At most `threshold` (0.7 when unset); absent = off.",
                "minimum": 0.3,
                "maximum": 0.9
              },
              "timeoutMs": {
                "type": "integer",
                "description": "End the turn after this much silence, in milliseconds (default 5000).",
                "minimum": 500,
                "maximum": 10000
              }
            },
            "description": "Flux end-of-turn detection (tModel flux-general-en or flux-general-multi; other models ignore it). Absent fields use the defaults; null or {} clears. Errors name the setting (`sttEndOfTurn.eager`).",
            "additionalProperties": false
          },
          "sttKeyterms": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Keyterm prompting (Nova-3 and Flux): product names, jargon and proper nouns to recognise, one word or phrase per item. Trimmed and de-duplicated; no control characters; null clears (stored as []).",
            "maxItems": 100
          },
          "language": {
            "type": "string",
            "description": "BCP-47 language, e.g. en-US.",
            "maxLength": 20
          },
          "languages": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "primary": {
                "type": "string",
                "enum": [
                  "en",
                  "es",
                  "fr",
                  "de",
                  "it",
                  "pt",
                  "nl",
                  "hi",
                  "ru",
                  "ja"
                ],
                "description": "The language calls start in unless the caller's language is known up front."
              },
              "supported": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Languages the call may switch to mid-call when the caller speaks them (always includes primary). More than one makes calls use the multilingual transcriber.",
                "maxItems": 10
              },
              "greetings": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 2000
                },
                "description": "First message per language code, used when the call starts in that language."
              },
              "voices": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 200
                },
                "description": "Voice per language code: a voice name (Maya) or a provider voice id of the assistant's voice provider, a cloned voice's id included."
              }
            },
            "required": [
              "primary"
            ],
            "description": "Multilingual calls: detect the caller's language and switch voice and replies mid-call. null clears. Errors name the setting (`languages.greetings.es`).",
            "additionalProperties": false
          },
          "endpoint": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "recording": {
            "type": "boolean",
            "description": "Record calls."
          },
          "recordingNotice": {
            "type": "boolean",
            "description": "Say a recording notice before the first message of recorded calls (default on)."
          },
          "recordingNoticeText": {
            "type": "string",
            "description": "The notice's wording; empty = \"This call may be recorded.\"",
            "maxLength": 300
          },
          "hipaa": {
            "type": "boolean",
            "description": "No transcript, recording or analysis leaves the call."
          },
          "waitSeconds": {
            "type": "number",
            "description": "Stored; not applied to live calls.",
            "minimum": 0,
            "maximum": 5
          },
          "interruptWords": {
            "type": "integer",
            "description": "Stored; not applied to live calls.",
            "minimum": 0,
            "maximum": 20
          },
          "silenceTimeout": {
            "type": "integer",
            "description": "Seconds of silence before hanging up.",
            "minimum": 1,
            "maximum": 600
          },
          "maxDuration": {
            "type": "integer",
            "description": "Maximum call length in minutes.",
            "minimum": 1,
            "maximum": 180
          },
          "voicemail": {
            "type": "boolean",
            "description": "Leave a voicemail on outbound calls that reach one."
          },
          "voicemailMessage": {
            "type": "string",
            "maxLength": 1000
          },
          "endPhrases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Phrases that end the call.",
            "maxItems": 50
          },
          "toolIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tool records the assistant may call.",
            "maxItems": 50
          },
          "serverUrl": {
            "type": "string",
            "description": "Receives webhooks and tool calls for this assistant.",
            "maxLength": 500
          },
          "transferNumber": {
            "type": "string",
            "description": "E.164 destination of the built-in transfer tool. A new value must pass the dial policy (no premium-rate prefixes; a country the organization may dial), else 400 on the field.",
            "maxLength": 20
          },
          "handoff": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "cold",
                  "warm"
                ],
                "description": "warm: the person hears a short summary first and presses 1 to take the call."
              },
              "team": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "members": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "number": {
                          "type": "string",
                          "description": "E.164; must pass the dial policy."
                        },
                        "name": {
                          "type": "string",
                          "maxLength": 60
                        }
                      },
                      "required": [
                        "number"
                      ]
                    },
                    "description": "1 to 10 people.",
                    "maxItems": 10
                  },
                  "ring": {
                    "type": "string",
                    "enum": [
                      "sequential",
                      "simultaneous"
                    ],
                    "description": "One after another in list order, or all at once (at most 5; the first to accept wins)."
                  },
                  "ringSecs": {
                    "type": "integer",
                    "description": "Seconds each ring lasts, 10 to 60 (default 20).",
                    "minimum": 10,
                    "maximum": 60
                  },
                  "holdMusicUrl": {
                    "type": "string",
                    "description": "https link to an .mp3 or .wav file played to the waiting caller; absent = Twilio's hold music."
                  },
                  "hours": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "window": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "{ start: \"HH:MM\", end: \"HH:MM\", days: [0..6] }"
                      },
                      "timeZone": {
                        "type": "string",
                        "description": "IANA zone."
                      }
                    },
                    "required": [
                      "window",
                      "timeZone"
                    ],
                    "description": "Outside these hours the assistant offers a callback instead."
                  }
                },
                "required": [
                  "members"
                ],
                "description": "Ring a team instead of transferNumber."
              },
              "fallback": {
                "type": "string",
                "enum": [
                  "assistant",
                  "callback"
                ],
                "description": "Nobody accepts: the caller goes back to the assistant, which (callback, the default) offers a callback."
              },
              "callbacks": {
                "type": "boolean",
                "description": "The assistant may schedule callbacks (request_callback; callback.requested webhook)."
              },
              "staffJoinLine": {
                "type": "string",
                "description": "Said to the caller when staff barge in from the console.",
                "maxLength": 200
              }
            },
            "description": "Human handoff desk (warm transfers, a team, callbacks); null = off.",
            "additionalProperties": false
          },
          "bookingIntegrationId": {
            "type": "string",
            "description": "Calendar integration the booking tool books into; absent = the organization's first connected one.",
            "maxLength": 64
          },
          "dailyBudget": {
            "type": "number",
            "description": "Dollars this assistant may spend per UTC day; 0 or absent = no budget.",
            "minimum": 0,
            "maximum": 100000
          },
          "verifyMessage": {
            "type": "string",
            "description": "Text of the caller verification SMS (tools.verify). Must contain {{code}}; empty or absent = \"Your verification code is {{code}}. It expires in 5 minutes.\"",
            "maxLength": 320
          },
          "latencyTargetMs": {
            "type": "integer",
            "description": "Latency the caller should hear at most, in milliseconds: 0 (no target) or 300 to 10000. The console flags the assistant and calls whose measured median turn latency (latencyStats.p50) is above it.",
            "minimum": 0,
            "maximum": 10000
          },
          "engine": {
            "type": "string",
            "enum": [
              "pipeline",
              "openai-realtime",
              "gemini-live"
            ],
            "description": "How calls run: pipeline (transcriber, model and voice; the default) or a speech-to-speech model (openai-realtime, gemini-live) configured by `realtime`. Realtime engines refuse HIPAA mode and cannot be in a squad or run a workflow (400 on `engine`)."
          },
          "realtime": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "model": {
                "type": "string",
                "description": "Realtime model: gpt-realtime, gpt-realtime-mini, gpt-4o-mini-realtime-preview (openai-realtime); gemini-2.5-flash-native-audio-preview-09-2025, gemini-live-2.5-flash-preview (gemini-live). Others need the organization's own OpenAI / Google key.",
                "maxLength": 80
              },
              "voice": {
                "type": "string",
                "description": "A voice of the engine: marin, cedar, alloy, ash, ballad, coral, echo, sage, shimmer, verse (OpenAI); Puck, Charon, Kore, Fenrir, Aoede, Leda, Orus, Zephyr (Gemini).",
                "maxLength": 40
              },
              "vad": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "server",
                      "semantic"
                    ],
                    "description": "OpenAI: pause based (server, default) or semantic turn detection."
                  },
                  "silenceMs": {
                    "type": "integer",
                    "description": "Pause that ends the caller's turn, ms.",
                    "minimum": 200,
                    "maximum": 3000
                  },
                  "threshold": {
                    "type": "number",
                    "description": "OpenAI pause based: speech threshold (default 0.5).",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "eagerness": {
                    "type": "string",
                    "enum": [
                      "low",
                      "medium",
                      "high",
                      "auto"
                    ],
                    "description": "OpenAI semantic: how eagerly the model answers."
                  }
                },
                "description": "Turn detection; absent fields use the engine's defaults.",
                "additionalProperties": false
              },
              "fallback": {
                "type": "string",
                "enum": [
                  "pipeline",
                  "end"
                ],
                "description": "The engine cannot connect at call start (5 s): run the call on the pipeline (default) or end it."
              }
            },
            "required": [
              "model",
              "voice"
            ],
            "description": "Speech-to-speech settings (required with a realtime engine); null clears. Errors name the setting (`realtime.voice`).",
            "additionalProperties": false
          },
          "kb": {
            "type": "string",
            "description": "Knowledge base name.",
            "maxLength": 120
          },
          "summaryPrompt": {
            "type": "string",
            "description": "Post-call summary instructions.",
            "maxLength": 2000
          },
          "evaluation": {
            "type": "string",
            "enum": [
              "pass",
              "numeric",
              "descriptive"
            ],
            "description": "Success evaluation type of the post-call analysis."
          },
          "analysisSchema": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Field name in the extracted data."
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "number",
                    "boolean",
                    "enum"
                  ]
                },
                "description": {
                  "type": "string",
                  "description": "What the model should extract."
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Allowed values when `type` is enum."
                }
              },
              "required": [
                "key",
                "type"
              ],
              "description": "One structured-data field of the post-call analysis.",
              "additionalProperties": false
            },
            "description": "Structured data the post-call analysis extracts.",
            "maxItems": 50
          },
          "followUpSms": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "template": {
                "type": "string",
                "description": "At most 480 characters; placeholders {{summary}}, {{assistant}}, {{customer}}, {{data.<key>}}. Required when enabled.",
                "maxLength": 480
              },
              "when": {
                "type": "string",
                "enum": [
                  "always",
                  "success",
                  "data"
                ],
                "description": "Default always."
              },
              "requireField": {
                "type": "string",
                "description": "The analysis field that must have a value when `when` is data."
              },
              "consent": {
                "type": "string",
                "enum": [
                  "implied",
                  "asked"
                ],
                "description": "Default asked: sends only when the analysis field sms_consent is true. implied texts inbound callers without asking (never outbound or campaign calls)."
              }
            },
            "required": [
              "enabled",
              "template"
            ],
            "description": "Text the caller after an analyzed phone call.",
            "additionalProperties": false
          },
          "payments": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "Adds collect_payment: a Stripe Checkout link from your own Stripe account (Provider keys → Payments during calls), texted to phone callers after their explicit yes or shown to web callers."
              },
              "maxAmount": {
                "type": "number",
                "description": "Largest amount per request in the first currency's main unit (two decimals at most).",
                "minimum": 0.01,
                "maximum": 100000
              },
              "currencies": {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^[a-z]{3}$"
                },
                "description": "Lower-case ISO 4217 codes (usd, eur, gbp, ...); the first is the default.",
                "minItems": 1,
                "maxItems": 10
              },
              "allowedDescriptions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "maxLength": 100
                },
                "description": "When set, a request's description must be one of these.",
                "maxItems": 20
              },
              "keypad": {
                "type": "boolean",
                "description": "Adds collect_card_by_keypad on phone calls through your own Twilio (Twilio <Pay> with your Pay connector)."
              }
            },
            "required": [
              "enabled",
              "maxAmount",
              "currencies"
            ],
            "description": "A compliance setting (admins; test keys may not change it). Never used on HIPAA calls; at most 2 payment requests per call.",
            "additionalProperties": false
          },
          "redaction": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "types": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "card",
                    "email",
                    "phone",
                    "address",
                    "ssn",
                    "iban",
                    "dob"
                  ]
                },
                "description": "What to mask; at least one (or the model pass) when enabled."
              },
              "model": {
                "type": "boolean",
                "description": "Also run a model pass for names and addresses the patterns missed."
              }
            },
            "required": [
              "enabled",
              "types"
            ],
            "description": "Mask personal data in what is stored and sent about calls.",
            "additionalProperties": false
          },
          "widget": {
            "type": "object",
            "properties": {
              "position": {
                "type": "string",
                "enum": [
                  "right",
                  "left"
                ],
                "description": "Corner of the floating button; default right."
              },
              "theme": {
                "type": "string",
                "enum": [
                  "light",
                  "dark"
                ],
                "description": "Default light."
              },
              "label": {
                "type": "string",
                "description": "Panel title (and button text unless buttonLabel is set); default Talk to us.",
                "maxLength": 60
              },
              "accent": {
                "type": "string",
                "description": "Accent color #rgb or #rrggbb (stored as lowercase #rrggbb): the button, focus ring and primary buttons; the text on it is white or near-black by WCAG contrast.",
                "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
              },
              "logo": {
                "type": "string",
                "description": "Header logo: an https URL (at most 2048 characters) or a data:image/png, jpeg or svg+xml base64 URL of at most 50 kB.",
                "maxLength": 51200
              },
              "welcome": {
                "type": "string",
                "description": "Plain text shown in the panel before the call starts.",
                "maxLength": 140
              },
              "buttonIcon": {
                "type": "string",
                "description": "mic (default), chat, phone, wave, or an https image URL."
              },
              "buttonLabel": {
                "type": "string",
                "description": "Button text next to the icon; default: the label.",
                "maxLength": 24
              },
              "poweredBy": {
                "type": "boolean",
                "description": "false hides the Powered by Voxa footer; default true."
              }
            },
            "description": "Embeddable widget appearance (the data-* attributes of /voxa-widget.js). Replaced as a whole on writes; defaults are left out; null or {} resets it.",
            "additionalProperties": false
          },
          "memory": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "Remember callers: the caller's contact is added to the prompt at call start (never under HIPAA)."
              },
              "template": {
                "type": "string",
                "description": "What the assistant is told, at most 600 characters; placeholders {{name}}, {{lastSeen}}, {{lastSummary}}, {{data}}, {{openItems}}, {{calls}}, {{data.<key>}}. Absent = the default template. The rendered text is cut to 800 characters.",
                "maxLength": 600
              }
            },
            "required": [
              "enabled"
            ],
            "description": "Returning-caller memory. Contacts are keyed by the caller's number (phone calls) or the web call's customerId.",
            "additionalProperties": false
          },
          "tools": {
            "type": "object",
            "properties": {
              "book": {
                "type": "boolean"
              },
              "transfer": {
                "type": "boolean"
              },
              "end": {
                "type": "boolean"
              },
              "order": {
                "type": "boolean"
              },
              "query": {
                "type": "boolean"
              },
              "dnc": {
                "type": "boolean"
              },
              "verify": {
                "type": "boolean"
              },
              "crm": {
                "type": "boolean"
              },
              "square": {
                "type": "boolean"
              },
              "jobber": {
                "type": "boolean"
              },
              "nexhealth": {
                "type": "boolean"
              }
            },
            "description": "Built-in tool switches. On writes a partial object is merged with the current value. `dnc` (add the caller to the do-not-call list) and `verify` (verify the caller by an SMS code through the organization's own Twilio) may be missing on older assistants (off).",
            "additionalProperties": false
          },
          "cost": {
            "type": "string",
            "description": "Estimated price per minute, recomputed from model, voiceProvider and tProvider on every write.",
            "readOnly": true,
            "example": "$0.11"
          },
          "latency": {
            "type": "string",
            "description": "Estimated end-to-end latency label.",
            "readOnly": true,
            "example": "~780 ms"
          },
          "lat": {
            "type": "number",
            "description": "Estimated end-to-end latency in milliseconds.",
            "readOnly": true
          },
          "pricingWarning": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set on save when the model, voice and transcriber setup would sell below Voxa's cost target on its own keys (components on your own provider keys exempt); null when fine. A warning only: the write succeeds and the same text comes back in the x-voxa-notice response header.",
            "readOnly": true
          }
        },
        "required": [
          "name"
        ],
        "description": "A new assistant: anything left out takes the console's defaults (the organization's first assistant's settings, as a Draft with an empty prompt). Unknown and read-only fields (id, orgId, updatedAt, transient) are rejected; cost, latency and lat are ignored.",
        "additionalProperties": false
      },
      "AssistantUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Up to 80 characters. Required on create.",
            "maxLength": 80
          },
          "color": {
            "type": "integer",
            "description": "Avatar color index.",
            "minimum": 0,
            "maximum": 3
          },
          "status": {
            "type": "string",
            "enum": [
              "Published",
              "Draft"
            ],
            "description": "New assistants start as Draft. Draft → Published saves the configuration as a new version."
          },
          "provider": {
            "type": "string",
            "enum": [
              "OpenAI",
              "Anthropic",
              "Google"
            ],
            "description": "Model provider."
          },
          "model": {
            "type": "string",
            "description": "Model id, e.g. gpt-4o. On Voxa's model keys only the priced models are accepted (gpt-4o, gpt-4o-mini, gpt-4.1, claude-sonnet-4, claude-haiku-4, gemini-2.5-flash, gemini-2.5-pro and a few more; a 400 on `model` lists them); with your own key for the provider any model id.",
            "maxLength": 80
          },
          "firstMessage": {
            "type": "string",
            "description": "What the assistant says first.",
            "maxLength": 2000
          },
          "prompt": {
            "type": "string",
            "description": "System prompt.",
            "maxLength": 20000
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2
          },
          "maxTokens": {
            "type": "integer",
            "description": "Stored; not applied to live calls.",
            "minimum": 1,
            "maximum": 32000
          },
          "voiceProvider": {
            "type": "string",
            "enum": [
              "Cartesia",
              "ElevenLabs",
              "Deepgram",
              "OpenAI"
            ]
          },
          "voice": {
            "type": "string",
            "description": "Voice name.",
            "maxLength": 80
          },
          "voiceId": {
            "type": "string",
            "description": "Provider voice id used instead of the mapped one (non-Deepgram voices).",
            "maxLength": 200
          },
          "speed": {
            "type": "number",
            "description": "Stored; not applied to live calls.",
            "minimum": 0.5,
            "maximum": 2
          },
          "denoise": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "backchannel": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "tProvider": {
            "type": "string",
            "enum": [
              "Deepgram",
              "AssemblyAI",
              "Google",
              "OpenAI"
            ],
            "description": "Transcriber provider."
          },
          "tModel": {
            "type": "string",
            "description": "Transcriber model. Deepgram: nova-3, nova-3-multi, flux-general-en, flux-general-multi or nova-2; a create without it gets the deployment's default (nova-3 unless configured); calls fall back to a model that covers the language.",
            "maxLength": 80
          },
          "sttEndOfTurn": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "threshold": {
                "type": "number",
                "description": "End-of-turn confidence (Deepgram's default 0.7): higher waits for clearer pauses, lower answers sooner.",
                "minimum": 0.5,
                "maximum": 1
              },
              "eager": {
                "type": "number",
                "description": "Eager end-of-turn confidence: the agent starts preparing a reply early. At most `threshold` (0.7 when unset); absent = off.",
                "minimum": 0.3,
                "maximum": 0.9
              },
              "timeoutMs": {
                "type": "integer",
                "description": "End the turn after this much silence, in milliseconds (default 5000).",
                "minimum": 500,
                "maximum": 10000
              }
            },
            "description": "Flux end-of-turn detection (tModel flux-general-en or flux-general-multi; other models ignore it). Absent fields use the defaults; null or {} clears. Errors name the setting (`sttEndOfTurn.eager`).",
            "additionalProperties": false
          },
          "sttKeyterms": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Keyterm prompting (Nova-3 and Flux): product names, jargon and proper nouns to recognise, one word or phrase per item. Trimmed and de-duplicated; no control characters; null clears (stored as []).",
            "maxItems": 100
          },
          "language": {
            "type": "string",
            "description": "BCP-47 language, e.g. en-US.",
            "maxLength": 20
          },
          "languages": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "primary": {
                "type": "string",
                "enum": [
                  "en",
                  "es",
                  "fr",
                  "de",
                  "it",
                  "pt",
                  "nl",
                  "hi",
                  "ru",
                  "ja"
                ],
                "description": "The language calls start in unless the caller's language is known up front."
              },
              "supported": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Languages the call may switch to mid-call when the caller speaks them (always includes primary). More than one makes calls use the multilingual transcriber.",
                "maxItems": 10
              },
              "greetings": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 2000
                },
                "description": "First message per language code, used when the call starts in that language."
              },
              "voices": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 200
                },
                "description": "Voice per language code: a voice name (Maya) or a provider voice id of the assistant's voice provider, a cloned voice's id included."
              }
            },
            "required": [
              "primary"
            ],
            "description": "Multilingual calls: detect the caller's language and switch voice and replies mid-call. null clears. Errors name the setting (`languages.greetings.es`).",
            "additionalProperties": false
          },
          "endpoint": {
            "type": "boolean",
            "description": "Stored; not applied to live calls."
          },
          "recording": {
            "type": "boolean",
            "description": "Record calls."
          },
          "recordingNotice": {
            "type": "boolean",
            "description": "Say a recording notice before the first message of recorded calls (default on)."
          },
          "recordingNoticeText": {
            "type": "string",
            "description": "The notice's wording; empty = \"This call may be recorded.\"",
            "maxLength": 300
          },
          "hipaa": {
            "type": "boolean",
            "description": "No transcript, recording or analysis leaves the call."
          },
          "waitSeconds": {
            "type": "number",
            "description": "Stored; not applied to live calls.",
            "minimum": 0,
            "maximum": 5
          },
          "interruptWords": {
            "type": "integer",
            "description": "Stored; not applied to live calls.",
            "minimum": 0,
            "maximum": 20
          },
          "silenceTimeout": {
            "type": "integer",
            "description": "Seconds of silence before hanging up.",
            "minimum": 1,
            "maximum": 600
          },
          "maxDuration": {
            "type": "integer",
            "description": "Maximum call length in minutes.",
            "minimum": 1,
            "maximum": 180
          },
          "voicemail": {
            "type": "boolean",
            "description": "Leave a voicemail on outbound calls that reach one."
          },
          "voicemailMessage": {
            "type": "string",
            "maxLength": 1000
          },
          "endPhrases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Phrases that end the call.",
            "maxItems": 50
          },
          "toolIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tool records the assistant may call.",
            "maxItems": 50
          },
          "serverUrl": {
            "type": "string",
            "description": "Receives webhooks and tool calls for this assistant.",
            "maxLength": 500
          },
          "transferNumber": {
            "type": "string",
            "description": "E.164 destination of the built-in transfer tool. A new value must pass the dial policy (no premium-rate prefixes; a country the organization may dial), else 400 on the field.",
            "maxLength": 20
          },
          "handoff": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "cold",
                  "warm"
                ],
                "description": "warm: the person hears a short summary first and presses 1 to take the call."
              },
              "team": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "members": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "number": {
                          "type": "string",
                          "description": "E.164; must pass the dial policy."
                        },
                        "name": {
                          "type": "string",
                          "maxLength": 60
                        }
                      },
                      "required": [
                        "number"
                      ]
                    },
                    "description": "1 to 10 people.",
                    "maxItems": 10
                  },
                  "ring": {
                    "type": "string",
                    "enum": [
                      "sequential",
                      "simultaneous"
                    ],
                    "description": "One after another in list order, or all at once (at most 5; the first to accept wins)."
                  },
                  "ringSecs": {
                    "type": "integer",
                    "description": "Seconds each ring lasts, 10 to 60 (default 20).",
                    "minimum": 10,
                    "maximum": 60
                  },
                  "holdMusicUrl": {
                    "type": "string",
                    "description": "https link to an .mp3 or .wav file played to the waiting caller; absent = Twilio's hold music."
                  },
                  "hours": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "window": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "{ start: \"HH:MM\", end: \"HH:MM\", days: [0..6] }"
                      },
                      "timeZone": {
                        "type": "string",
                        "description": "IANA zone."
                      }
                    },
                    "required": [
                      "window",
                      "timeZone"
                    ],
                    "description": "Outside these hours the assistant offers a callback instead."
                  }
                },
                "required": [
                  "members"
                ],
                "description": "Ring a team instead of transferNumber."
              },
              "fallback": {
                "type": "string",
                "enum": [
                  "assistant",
                  "callback"
                ],
                "description": "Nobody accepts: the caller goes back to the assistant, which (callback, the default) offers a callback."
              },
              "callbacks": {
                "type": "boolean",
                "description": "The assistant may schedule callbacks (request_callback; callback.requested webhook)."
              },
              "staffJoinLine": {
                "type": "string",
                "description": "Said to the caller when staff barge in from the console.",
                "maxLength": 200
              }
            },
            "description": "Human handoff desk (warm transfers, a team, callbacks); null = off.",
            "additionalProperties": false
          },
          "bookingIntegrationId": {
            "type": "string",
            "description": "Calendar integration the booking tool books into; absent = the organization's first connected one.",
            "maxLength": 64
          },
          "dailyBudget": {
            "type": "number",
            "description": "Dollars this assistant may spend per UTC day; 0 or absent = no budget.",
            "minimum": 0,
            "maximum": 100000
          },
          "verifyMessage": {
            "type": "string",
            "description": "Text of the caller verification SMS (tools.verify). Must contain {{code}}; empty or absent = \"Your verification code is {{code}}. It expires in 5 minutes.\"",
            "maxLength": 320
          },
          "latencyTargetMs": {
            "type": "integer",
            "description": "Latency the caller should hear at most, in milliseconds: 0 (no target) or 300 to 10000. The console flags the assistant and calls whose measured median turn latency (latencyStats.p50) is above it.",
            "minimum": 0,
            "maximum": 10000
          },
          "engine": {
            "type": "string",
            "enum": [
              "pipeline",
              "openai-realtime",
              "gemini-live"
            ],
            "description": "How calls run: pipeline (transcriber, model and voice; the default) or a speech-to-speech model (openai-realtime, gemini-live) configured by `realtime`. Realtime engines refuse HIPAA mode and cannot be in a squad or run a workflow (400 on `engine`)."
          },
          "realtime": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "model": {
                "type": "string",
                "description": "Realtime model: gpt-realtime, gpt-realtime-mini, gpt-4o-mini-realtime-preview (openai-realtime); gemini-2.5-flash-native-audio-preview-09-2025, gemini-live-2.5-flash-preview (gemini-live). Others need the organization's own OpenAI / Google key.",
                "maxLength": 80
              },
              "voice": {
                "type": "string",
                "description": "A voice of the engine: marin, cedar, alloy, ash, ballad, coral, echo, sage, shimmer, verse (OpenAI); Puck, Charon, Kore, Fenrir, Aoede, Leda, Orus, Zephyr (Gemini).",
                "maxLength": 40
              },
              "vad": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "server",
                      "semantic"
                    ],
                    "description": "OpenAI: pause based (server, default) or semantic turn detection."
                  },
                  "silenceMs": {
                    "type": "integer",
                    "description": "Pause that ends the caller's turn, ms.",
                    "minimum": 200,
                    "maximum": 3000
                  },
                  "threshold": {
                    "type": "number",
                    "description": "OpenAI pause based: speech threshold (default 0.5).",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "eagerness": {
                    "type": "string",
                    "enum": [
                      "low",
                      "medium",
                      "high",
                      "auto"
                    ],
                    "description": "OpenAI semantic: how eagerly the model answers."
                  }
                },
                "description": "Turn detection; absent fields use the engine's defaults.",
                "additionalProperties": false
              },
              "fallback": {
                "type": "string",
                "enum": [
                  "pipeline",
                  "end"
                ],
                "description": "The engine cannot connect at call start (5 s): run the call on the pipeline (default) or end it."
              }
            },
            "required": [
              "model",
              "voice"
            ],
            "description": "Speech-to-speech settings (required with a realtime engine); null clears. Errors name the setting (`realtime.voice`).",
            "additionalProperties": false
          },
          "kb": {
            "type": "string",
            "description": "Knowledge base name.",
            "maxLength": 120
          },
          "summaryPrompt": {
            "type": "string",
            "description": "Post-call summary instructions.",
            "maxLength": 2000
          },
          "evaluation": {
            "type": "string",
            "enum": [
              "pass",
              "numeric",
              "descriptive"
            ],
            "description": "Success evaluation type of the post-call analysis."
          },
          "analysisSchema": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Field name in the extracted data."
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "number",
                    "boolean",
                    "enum"
                  ]
                },
                "description": {
                  "type": "string",
                  "description": "What the model should extract."
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Allowed values when `type` is enum."
                }
              },
              "required": [
                "key",
                "type"
              ],
              "description": "One structured-data field of the post-call analysis.",
              "additionalProperties": false
            },
            "description": "Structured data the post-call analysis extracts.",
            "maxItems": 50
          },
          "followUpSms": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "template": {
                "type": "string",
                "description": "At most 480 characters; placeholders {{summary}}, {{assistant}}, {{customer}}, {{data.<key>}}. Required when enabled.",
                "maxLength": 480
              },
              "when": {
                "type": "string",
                "enum": [
                  "always",
                  "success",
                  "data"
                ],
                "description": "Default always."
              },
              "requireField": {
                "type": "string",
                "description": "The analysis field that must have a value when `when` is data."
              },
              "consent": {
                "type": "string",
                "enum": [
                  "implied",
                  "asked"
                ],
                "description": "Default asked: sends only when the analysis field sms_consent is true. implied texts inbound callers without asking (never outbound or campaign calls)."
              }
            },
            "required": [
              "enabled",
              "template"
            ],
            "description": "Text the caller after an analyzed phone call.",
            "additionalProperties": false
          },
          "payments": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "Adds collect_payment: a Stripe Checkout link from your own Stripe account (Provider keys → Payments during calls), texted to phone callers after their explicit yes or shown to web callers."
              },
              "maxAmount": {
                "type": "number",
                "description": "Largest amount per request in the first currency's main unit (two decimals at most).",
                "minimum": 0.01,
                "maximum": 100000
              },
              "currencies": {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^[a-z]{3}$"
                },
                "description": "Lower-case ISO 4217 codes (usd, eur, gbp, ...); the first is the default.",
                "minItems": 1,
                "maxItems": 10
              },
              "allowedDescriptions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "maxLength": 100
                },
                "description": "When set, a request's description must be one of these.",
                "maxItems": 20
              },
              "keypad": {
                "type": "boolean",
                "description": "Adds collect_card_by_keypad on phone calls through your own Twilio (Twilio <Pay> with your Pay connector)."
              }
            },
            "required": [
              "enabled",
              "maxAmount",
              "currencies"
            ],
            "description": "A compliance setting (admins; test keys may not change it). Never used on HIPAA calls; at most 2 payment requests per call.",
            "additionalProperties": false
          },
          "redaction": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "types": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "card",
                    "email",
                    "phone",
                    "address",
                    "ssn",
                    "iban",
                    "dob"
                  ]
                },
                "description": "What to mask; at least one (or the model pass) when enabled."
              },
              "model": {
                "type": "boolean",
                "description": "Also run a model pass for names and addresses the patterns missed."
              }
            },
            "required": [
              "enabled",
              "types"
            ],
            "description": "Mask personal data in what is stored and sent about calls.",
            "additionalProperties": false
          },
          "widget": {
            "type": "object",
            "properties": {
              "position": {
                "type": "string",
                "enum": [
                  "right",
                  "left"
                ],
                "description": "Corner of the floating button; default right."
              },
              "theme": {
                "type": "string",
                "enum": [
                  "light",
                  "dark"
                ],
                "description": "Default light."
              },
              "label": {
                "type": "string",
                "description": "Panel title (and button text unless buttonLabel is set); default Talk to us.",
                "maxLength": 60
              },
              "accent": {
                "type": "string",
                "description": "Accent color #rgb or #rrggbb (stored as lowercase #rrggbb): the button, focus ring and primary buttons; the text on it is white or near-black by WCAG contrast.",
                "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
              },
              "logo": {
                "type": "string",
                "description": "Header logo: an https URL (at most 2048 characters) or a data:image/png, jpeg or svg+xml base64 URL of at most 50 kB.",
                "maxLength": 51200
              },
              "welcome": {
                "type": "string",
                "description": "Plain text shown in the panel before the call starts.",
                "maxLength": 140
              },
              "buttonIcon": {
                "type": "string",
                "description": "mic (default), chat, phone, wave, or an https image URL."
              },
              "buttonLabel": {
                "type": "string",
                "description": "Button text next to the icon; default: the label.",
                "maxLength": 24
              },
              "poweredBy": {
                "type": "boolean",
                "description": "false hides the Powered by Voxa footer; default true."
              }
            },
            "description": "Embeddable widget appearance (the data-* attributes of /voxa-widget.js). Replaced as a whole on writes; defaults are left out; null or {} resets it.",
            "additionalProperties": false
          },
          "memory": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "Remember callers: the caller's contact is added to the prompt at call start (never under HIPAA)."
              },
              "template": {
                "type": "string",
                "description": "What the assistant is told, at most 600 characters; placeholders {{name}}, {{lastSeen}}, {{lastSummary}}, {{data}}, {{openItems}}, {{calls}}, {{data.<key>}}. Absent = the default template. The rendered text is cut to 800 characters.",
                "maxLength": 600
              }
            },
            "required": [
              "enabled"
            ],
            "description": "Returning-caller memory. Contacts are keyed by the caller's number (phone calls) or the web call's customerId.",
            "additionalProperties": false
          },
          "tools": {
            "type": "object",
            "properties": {
              "book": {
                "type": "boolean"
              },
              "transfer": {
                "type": "boolean"
              },
              "end": {
                "type": "boolean"
              },
              "order": {
                "type": "boolean"
              },
              "query": {
                "type": "boolean"
              },
              "dnc": {
                "type": "boolean"
              },
              "verify": {
                "type": "boolean"
              },
              "crm": {
                "type": "boolean"
              },
              "square": {
                "type": "boolean"
              },
              "jobber": {
                "type": "boolean"
              },
              "nexhealth": {
                "type": "boolean"
              }
            },
            "description": "Built-in tool switches. On writes a partial object is merged with the current value. `dnc` (add the caller to the do-not-call list) and `verify` (verify the caller by an SMS code through the organization's own Twilio) may be missing on older assistants (off).",
            "additionalProperties": false
          },
          "cost": {
            "type": "string",
            "description": "Estimated price per minute, recomputed from model, voiceProvider and tProvider on every write.",
            "readOnly": true,
            "example": "$0.11"
          },
          "latency": {
            "type": "string",
            "description": "Estimated end-to-end latency label.",
            "readOnly": true,
            "example": "~780 ms"
          },
          "lat": {
            "type": "number",
            "description": "Estimated end-to-end latency in milliseconds.",
            "readOnly": true
          },
          "pricingWarning": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set on save when the model, voice and transcriber setup would sell below Voxa's cost target on its own keys (components on your own provider keys exempt); null when fine. A warning only: the write succeeds and the same text comes back in the x-voxa-notice response header.",
            "readOnly": true
          }
        },
        "description": "Fields to change. Same rules as create; tools is merged with the current value.",
        "additionalProperties": false
      },
      "AssistantList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Assistant"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of Assistant records."
      },
      "AnalysisField": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Field name in the extracted data."
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean",
              "enum"
            ]
          },
          "description": {
            "type": "string",
            "description": "What the model should extract."
          },
          "values": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Allowed values when `type` is enum."
          }
        },
        "required": [
          "key",
          "type"
        ],
        "description": "One structured-data field of the post-call analysis.",
        "additionalProperties": false
      },
      "TranscriptLine": {
        "type": "object",
        "properties": {
          "who": {
            "type": "string",
            "enum": [
              "Assistant",
              "Caller"
            ]
          },
          "text": {
            "type": "string"
          }
        },
        "required": [
          "who",
          "text"
        ]
      },
      "Call": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "call_9a1"
          },
          "orgId": {
            "type": "string",
            "description": "Owning organization."
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant that answered (the video agent for Full-mode video calls)."
          },
          "asst": {
            "type": "string",
            "description": "Its name at call time."
          },
          "customer": {
            "type": "string",
            "description": "The caller's number or label."
          },
          "type": {
            "type": "string",
            "enum": [
              "Inbound",
              "Outbound",
              "Web"
            ]
          },
          "reason": {
            "type": "string",
            "description": "How the call ended (\"In progress\" while live, \"Interrupted\" when the voice process stopped mid-call)."
          },
          "kind": {
            "type": "string",
            "enum": [
              "ok",
              "transfer",
              "fail",
              "voicemail"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "live",
              "interrupted",
              "completed",
              "failed",
              "transferred",
              "voicemail"
            ],
            "description": "Derived from reason and kind."
          },
          "secs": {
            "type": "number",
            "description": "Duration in seconds."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "costN": {
            "type": "number",
            "description": "Cost in dollars."
          },
          "summary": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "k": {
                  "type": "string"
                },
                "v": {
                  "type": "string"
                }
              },
              "required": [
                "k",
                "v"
              ]
            },
            "description": "Extracted data."
          },
          "evalPass": {
            "type": "boolean"
          },
          "recordingUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "`/api/v1/calls/{id}/recording` when a recording exists."
          },
          "evaluation": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "pass",
                  "numeric",
                  "descriptive"
                ]
              },
              "score": {
                "type": "number"
              },
              "text": {
                "type": "string"
              }
            },
            "required": [
              "type"
            ],
            "description": "Outcome of the success evaluation."
          },
          "analyzedAt": {
            "type": "string",
            "description": "Last post-call analysis.",
            "format": "date-time"
          },
          "analysis": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string"
              },
              "model": {
                "type": "string"
              }
            },
            "required": [
              "provider",
              "model"
            ],
            "description": "Which model produced the analysis."
          },
          "hipaa": {
            "type": "boolean",
            "description": "The call ran under HIPAA rules: `customer` (and any data value that is the caller's number) reads \"[redacted]\"."
          },
          "video": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "heygen"
                ]
              },
              "mode": {
                "type": "string",
                "enum": [
                  "lite",
                  "full"
                ],
                "description": "lite: the assistant talks through the voice server; full: HeyGen runs speech, model and voice. Absent on older rows = lite."
              },
              "videoAgentId": {
                "type": "string"
              },
              "avatarId": {
                "type": "string"
              },
              "avatarName": {
                "type": "string"
              },
              "sessionId": {
                "type": "string"
              },
              "sandbox": {
                "type": "boolean"
              },
              "error": {
                "type": "string",
                "description": "Set when the avatar could not start or dropped and the call continued as audio."
              }
            },
            "required": [
              "provider",
              "videoAgentId",
              "avatarId",
              "avatarName"
            ],
            "description": "Video facts of a call made with a video agent."
          },
          "promptVersion": {
            "type": "integer",
            "description": "Published version of the assistant (or video agent) that answered."
          },
          "promptModified": {
            "type": "boolean",
            "description": "The live configuration differed from that version."
          },
          "promptVersionOf": {
            "type": "string",
            "description": "The record promptVersion belongs to."
          },
          "followUp": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "sending",
                  "sent",
                  "skipped",
                  "failed"
                ]
              },
              "reason": {
                "type": "string"
              },
              "sid": {
                "type": "string",
                "description": "Twilio Message SID."
              },
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "body": {
                "type": "string"
              },
              "to": {
                "type": "string"
              }
            },
            "required": [
              "status",
              "at"
            ],
            "description": "What happened to the call's follow-up SMS."
          },
          "verified": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "method": {
                "type": "string",
                "enum": [
                  "sms"
                ]
              },
              "to": {
                "type": "string",
                "description": "The number the code went to, masked to its last four digits."
              }
            },
            "required": [
              "at",
              "method",
              "to"
            ],
            "description": "The caller passed the SMS code check during the call (tools.verify)."
          },
          "booking": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "google_calendar",
                  "calcom"
                ]
              },
              "integrationId": {
                "type": "string"
              },
              "eventId": {
                "type": "string"
              },
              "start": {
                "type": "string",
                "format": "date-time"
              },
              "end": {
                "type": "string",
                "format": "date-time"
              },
              "title": {
                "type": "string"
              },
              "timeZone": {
                "type": "string"
              },
              "link": {
                "type": "string"
              }
            },
            "required": [
              "provider",
              "eventId",
              "start",
              "end",
              "title"
            ],
            "description": "The appointment the assistant booked during the call."
          },
          "redacted": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer",
                "description": "Masked spans in total."
              },
              "kinds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Kinds masked at least once (the redaction types, or name)."
              },
              "byKind": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                }
              },
              "types": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "card",
                    "email",
                    "phone",
                    "address",
                    "ssn",
                    "iban",
                    "dob"
                  ]
                }
              },
              "model": {
                "type": "string",
                "enum": [
                  "ok",
                  "failed",
                  "no_key"
                ]
              }
            },
            "required": [
              "count",
              "kinds"
            ],
            "description": "Personal data masked in the stored transcript and summary."
          },
          "test": {
            "type": "boolean",
            "description": "A test call (test key or test public key): never charged."
          },
          "customerId": {
            "type": "string",
            "description": "Web calls: the caller's id in your own system, as passed when the call was started (SDK / widget customerId, POST /api/v1/web-calls)."
          },
          "latencyStats": {
            "type": "object",
            "properties": {
              "p50": {
                "type": "integer",
                "description": "Median turn latency in ms (end of the caller's speech to the first reply audio)."
              },
              "p95": {
                "type": "integer",
                "description": "95th percentile turn latency in ms."
              },
              "n": {
                "type": "integer",
                "description": "Turns counted (turns the caller talked over are left out)."
              },
              "listenP50": {
                "type": "integer",
                "description": "Median of listenMs."
              },
              "thinkP50": {
                "type": "integer",
                "description": "Median of thinkMs."
              },
              "speakP50": {
                "type": "integer",
                "description": "Median of speakMs."
              },
              "interrupted": {
                "type": "integer",
                "description": "Turns left out because the caller talked over the reply."
              }
            },
            "required": [
              "p50",
              "p95",
              "n"
            ],
            "description": "Nearest-rank percentiles over the call's measured turns, as measured on the voice server. Absent on Full video calls and calls without a completed turn."
          },
          "engine": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "pipeline",
                  "openai-realtime",
                  "gemini-live"
                ],
                "description": "The engine that ran the call."
              },
              "requested": {
                "type": "string",
                "enum": [
                  "pipeline",
                  "openai-realtime",
                  "gemini-live"
                ],
                "description": "The assistant's engine when the call ran on another one (a fallback)."
              },
              "model": {
                "type": "string",
                "description": "The realtime model."
              },
              "voice": {
                "type": "string",
                "description": "The realtime voice."
              },
              "fallback": {
                "type": "string",
                "description": "Why the call ran on the pipeline: timeout, connect_failed, no_key, workflow, squad or hipaa."
              },
              "error": {
                "type": "string",
                "description": "Why the realtime engine stopped during the call."
              },
              "connectMs": {
                "type": "integer",
                "description": "Milliseconds the realtime engine took to connect."
              }
            },
            "required": [
              "kind"
            ],
            "description": "The call engine (assistant `engine`): absent on pipeline calls of pipeline assistants."
          },
          "topic": {
            "type": "string",
            "description": "What the call was mainly about: a label from the organization's topic list, written by the post-call analysis (renamed and merged topics show their current label)."
          },
          "sentiment": {
            "type": "number",
            "description": "The caller's overall sentiment from -1 (very negative) to 1 (very positive), written by the post-call analysis.",
            "minimum": -1,
            "maximum": 1
          },
          "retained": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "description": "When the content was removed.",
                "format": "date-time"
              },
              "by": {
                "type": "string",
                "enum": [
                  "retention",
                  "erasure"
                ],
                "description": "The organization's retention period, or an erasure request (POST /api/v1/privacy/erase)."
              },
              "removed": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Fields removed or masked (transcript, summary, data, customer, recording, ...)."
              },
              "kept": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Fields kept on purpose: billing facts and non-personal aggregates."
              }
            },
            "required": [
              "at",
              "by",
              "removed",
              "kept"
            ],
            "description": "Present when the call's personal content was removed by data retention or an erasure: the transcript is empty, the summary blank, the number masked or \"[erased]\"."
          },
          "scrubbedAt": {
            "type": "string",
            "description": "When the call's personal content (transcript, summary, caller data, recording) was removed.",
            "format": "date-time"
          },
          "score": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer",
                "description": "Weighted 0..100."
              },
              "criteria": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "score": {
                      "type": "integer",
                      "description": "0..100."
                    },
                    "pass": {
                      "type": "boolean"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "score",
                    "pass"
                  ]
                }
              },
              "scorecardId": {
                "type": "string"
              },
              "at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "total",
              "criteria"
            ],
            "description": "The scorecard result of the post-call analysis."
          },
          "flagged": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "reasons": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "low_score",
                    "negative_sentiment",
                    "keyword"
                  ]
                }
              },
              "keywords": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "notified": {
                "type": "boolean",
                "description": "The alerts went out (false when the hourly alert limit or the scorecard held them back)."
              }
            },
            "required": [
              "at",
              "reasons",
              "notified"
            ],
            "description": "The call tripped a quality alert."
          },
          "experiment": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The A/B test."
              },
              "arm": {
                "type": "string",
                "enum": [
                  "A",
                  "B"
                ]
              }
            },
            "required": [
              "id",
              "arm"
            ],
            "description": "The prompt A/B test arm the call ran on."
          },
          "languages": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "description": "The language the call started in."
              },
              "detected": {
                "type": "string",
                "description": "The caller's language as last detected."
              },
              "switches": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "atSec": {
                      "type": "integer",
                      "description": "Seconds from the call start."
                    }
                  },
                  "required": [
                    "from",
                    "to",
                    "atSec"
                  ]
                },
                "description": "Mid-call language switches."
              }
            },
            "required": [
              "start",
              "switches"
            ],
            "description": "Multilingual assistants: the call's languages."
          },
          "recordingSweptAt": {
            "type": "string",
            "description": "When the recording retention period was applied to the call.",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "assistantId",
          "asst",
          "customer",
          "type",
          "reason",
          "kind",
          "status",
          "secs",
          "startedAt",
          "costN",
          "summary",
          "data",
          "evalPass",
          "recordingUrl"
        ],
        "description": "A call as listed (without transcript)."
      },
      "CallDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "call_9a1"
          },
          "orgId": {
            "type": "string",
            "description": "Owning organization."
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant that answered (the video agent for Full-mode video calls)."
          },
          "asst": {
            "type": "string",
            "description": "Its name at call time."
          },
          "customer": {
            "type": "string",
            "description": "The caller's number or label."
          },
          "type": {
            "type": "string",
            "enum": [
              "Inbound",
              "Outbound",
              "Web"
            ]
          },
          "reason": {
            "type": "string",
            "description": "How the call ended (\"In progress\" while live, \"Interrupted\" when the voice process stopped mid-call)."
          },
          "kind": {
            "type": "string",
            "enum": [
              "ok",
              "transfer",
              "fail",
              "voicemail"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "live",
              "interrupted",
              "completed",
              "failed",
              "transferred",
              "voicemail"
            ],
            "description": "Derived from reason and kind."
          },
          "secs": {
            "type": "number",
            "description": "Duration in seconds."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "costN": {
            "type": "number",
            "description": "Cost in dollars."
          },
          "summary": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "k": {
                  "type": "string"
                },
                "v": {
                  "type": "string"
                }
              },
              "required": [
                "k",
                "v"
              ]
            },
            "description": "Extracted data."
          },
          "evalPass": {
            "type": "boolean"
          },
          "recordingUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "`/api/v1/calls/{id}/recording` when a recording exists."
          },
          "evaluation": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "pass",
                  "numeric",
                  "descriptive"
                ]
              },
              "score": {
                "type": "number"
              },
              "text": {
                "type": "string"
              }
            },
            "required": [
              "type"
            ],
            "description": "Outcome of the success evaluation."
          },
          "analyzedAt": {
            "type": "string",
            "description": "Last post-call analysis.",
            "format": "date-time"
          },
          "analysis": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string"
              },
              "model": {
                "type": "string"
              }
            },
            "required": [
              "provider",
              "model"
            ],
            "description": "Which model produced the analysis."
          },
          "hipaa": {
            "type": "boolean",
            "description": "The call ran under HIPAA rules: `customer` (and any data value that is the caller's number) reads \"[redacted]\"."
          },
          "video": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "heygen"
                ]
              },
              "mode": {
                "type": "string",
                "enum": [
                  "lite",
                  "full"
                ],
                "description": "lite: the assistant talks through the voice server; full: HeyGen runs speech, model and voice. Absent on older rows = lite."
              },
              "videoAgentId": {
                "type": "string"
              },
              "avatarId": {
                "type": "string"
              },
              "avatarName": {
                "type": "string"
              },
              "sessionId": {
                "type": "string"
              },
              "sandbox": {
                "type": "boolean"
              },
              "error": {
                "type": "string",
                "description": "Set when the avatar could not start or dropped and the call continued as audio."
              }
            },
            "required": [
              "provider",
              "videoAgentId",
              "avatarId",
              "avatarName"
            ],
            "description": "Video facts of a call made with a video agent."
          },
          "promptVersion": {
            "type": "integer",
            "description": "Published version of the assistant (or video agent) that answered."
          },
          "promptModified": {
            "type": "boolean",
            "description": "The live configuration differed from that version."
          },
          "promptVersionOf": {
            "type": "string",
            "description": "The record promptVersion belongs to."
          },
          "followUp": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "sending",
                  "sent",
                  "skipped",
                  "failed"
                ]
              },
              "reason": {
                "type": "string"
              },
              "sid": {
                "type": "string",
                "description": "Twilio Message SID."
              },
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "body": {
                "type": "string"
              },
              "to": {
                "type": "string"
              }
            },
            "required": [
              "status",
              "at"
            ],
            "description": "What happened to the call's follow-up SMS."
          },
          "verified": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "method": {
                "type": "string",
                "enum": [
                  "sms"
                ]
              },
              "to": {
                "type": "string",
                "description": "The number the code went to, masked to its last four digits."
              }
            },
            "required": [
              "at",
              "method",
              "to"
            ],
            "description": "The caller passed the SMS code check during the call (tools.verify)."
          },
          "booking": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "google_calendar",
                  "calcom"
                ]
              },
              "integrationId": {
                "type": "string"
              },
              "eventId": {
                "type": "string"
              },
              "start": {
                "type": "string",
                "format": "date-time"
              },
              "end": {
                "type": "string",
                "format": "date-time"
              },
              "title": {
                "type": "string"
              },
              "timeZone": {
                "type": "string"
              },
              "link": {
                "type": "string"
              }
            },
            "required": [
              "provider",
              "eventId",
              "start",
              "end",
              "title"
            ],
            "description": "The appointment the assistant booked during the call."
          },
          "redacted": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer",
                "description": "Masked spans in total."
              },
              "kinds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Kinds masked at least once (the redaction types, or name)."
              },
              "byKind": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                }
              },
              "types": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "card",
                    "email",
                    "phone",
                    "address",
                    "ssn",
                    "iban",
                    "dob"
                  ]
                }
              },
              "model": {
                "type": "string",
                "enum": [
                  "ok",
                  "failed",
                  "no_key"
                ]
              }
            },
            "required": [
              "count",
              "kinds"
            ],
            "description": "Personal data masked in the stored transcript and summary."
          },
          "test": {
            "type": "boolean",
            "description": "A test call (test key or test public key): never charged."
          },
          "customerId": {
            "type": "string",
            "description": "Web calls: the caller's id in your own system, as passed when the call was started (SDK / widget customerId, POST /api/v1/web-calls)."
          },
          "latencyStats": {
            "type": "object",
            "properties": {
              "p50": {
                "type": "integer",
                "description": "Median turn latency in ms (end of the caller's speech to the first reply audio)."
              },
              "p95": {
                "type": "integer",
                "description": "95th percentile turn latency in ms."
              },
              "n": {
                "type": "integer",
                "description": "Turns counted (turns the caller talked over are left out)."
              },
              "listenP50": {
                "type": "integer",
                "description": "Median of listenMs."
              },
              "thinkP50": {
                "type": "integer",
                "description": "Median of thinkMs."
              },
              "speakP50": {
                "type": "integer",
                "description": "Median of speakMs."
              },
              "interrupted": {
                "type": "integer",
                "description": "Turns left out because the caller talked over the reply."
              }
            },
            "required": [
              "p50",
              "p95",
              "n"
            ],
            "description": "Nearest-rank percentiles over the call's measured turns, as measured on the voice server. Absent on Full video calls and calls without a completed turn."
          },
          "engine": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "pipeline",
                  "openai-realtime",
                  "gemini-live"
                ],
                "description": "The engine that ran the call."
              },
              "requested": {
                "type": "string",
                "enum": [
                  "pipeline",
                  "openai-realtime",
                  "gemini-live"
                ],
                "description": "The assistant's engine when the call ran on another one (a fallback)."
              },
              "model": {
                "type": "string",
                "description": "The realtime model."
              },
              "voice": {
                "type": "string",
                "description": "The realtime voice."
              },
              "fallback": {
                "type": "string",
                "description": "Why the call ran on the pipeline: timeout, connect_failed, no_key, workflow, squad or hipaa."
              },
              "error": {
                "type": "string",
                "description": "Why the realtime engine stopped during the call."
              },
              "connectMs": {
                "type": "integer",
                "description": "Milliseconds the realtime engine took to connect."
              }
            },
            "required": [
              "kind"
            ],
            "description": "The call engine (assistant `engine`): absent on pipeline calls of pipeline assistants."
          },
          "topic": {
            "type": "string",
            "description": "What the call was mainly about: a label from the organization's topic list, written by the post-call analysis (renamed and merged topics show their current label)."
          },
          "sentiment": {
            "type": "number",
            "description": "The caller's overall sentiment from -1 (very negative) to 1 (very positive), written by the post-call analysis.",
            "minimum": -1,
            "maximum": 1
          },
          "retained": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "description": "When the content was removed.",
                "format": "date-time"
              },
              "by": {
                "type": "string",
                "enum": [
                  "retention",
                  "erasure"
                ],
                "description": "The organization's retention period, or an erasure request (POST /api/v1/privacy/erase)."
              },
              "removed": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Fields removed or masked (transcript, summary, data, customer, recording, ...)."
              },
              "kept": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Fields kept on purpose: billing facts and non-personal aggregates."
              }
            },
            "required": [
              "at",
              "by",
              "removed",
              "kept"
            ],
            "description": "Present when the call's personal content was removed by data retention or an erasure: the transcript is empty, the summary blank, the number masked or \"[erased]\"."
          },
          "scrubbedAt": {
            "type": "string",
            "description": "When the call's personal content (transcript, summary, caller data, recording) was removed.",
            "format": "date-time"
          },
          "score": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer",
                "description": "Weighted 0..100."
              },
              "criteria": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "score": {
                      "type": "integer",
                      "description": "0..100."
                    },
                    "pass": {
                      "type": "boolean"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "score",
                    "pass"
                  ]
                }
              },
              "scorecardId": {
                "type": "string"
              },
              "at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "total",
              "criteria"
            ],
            "description": "The scorecard result of the post-call analysis."
          },
          "flagged": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "reasons": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "low_score",
                    "negative_sentiment",
                    "keyword"
                  ]
                }
              },
              "keywords": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "notified": {
                "type": "boolean",
                "description": "The alerts went out (false when the hourly alert limit or the scorecard held them back)."
              }
            },
            "required": [
              "at",
              "reasons",
              "notified"
            ],
            "description": "The call tripped a quality alert."
          },
          "experiment": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The A/B test."
              },
              "arm": {
                "type": "string",
                "enum": [
                  "A",
                  "B"
                ]
              }
            },
            "required": [
              "id",
              "arm"
            ],
            "description": "The prompt A/B test arm the call ran on."
          },
          "languages": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "description": "The language the call started in."
              },
              "detected": {
                "type": "string",
                "description": "The caller's language as last detected."
              },
              "switches": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "atSec": {
                      "type": "integer",
                      "description": "Seconds from the call start."
                    }
                  },
                  "required": [
                    "from",
                    "to",
                    "atSec"
                  ]
                },
                "description": "Mid-call language switches."
              }
            },
            "required": [
              "start",
              "switches"
            ],
            "description": "Multilingual assistants: the call's languages."
          },
          "recordingSweptAt": {
            "type": "string",
            "description": "When the recording retention period was applied to the call.",
            "format": "date-time"
          },
          "transcript": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TranscriptLine"
            }
          },
          "turns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "integer",
                  "description": "When the caller's line was transcribed, in ms from the call start."
                },
                "line": {
                  "type": "integer",
                  "description": "Index of the caller's line in transcript (absent in HIPAA mode)."
                },
                "listenMs": {
                  "type": "integer",
                  "description": "End of the caller's speech (last voiced caller audio) to the caller's transcript; absent for typed lines."
                },
                "thinkMs": {
                  "type": "integer",
                  "description": "Transcript to first reply audio, minus tool and voice time."
                },
                "speakMs": {
                  "type": "integer",
                  "description": "The voice's time to first audio."
                },
                "totalMs": {
                  "type": "integer",
                  "description": "End of the caller's speech (or the transcript) to the first reply audio."
                },
                "toolMs": {
                  "type": "integer",
                  "description": "Time in function calls before the reply."
                },
                "interrupted": {
                  "type": "boolean",
                  "description": "The caller talked over the reply; not counted in latencyStats."
                }
              },
              "required": [
                "at",
                "totalMs"
              ]
            },
            "description": "Measured latency per turn: at most 200 (the first 20 and the last 180)."
          }
        },
        "required": [
          "id",
          "assistantId",
          "asst",
          "customer",
          "type",
          "reason",
          "kind",
          "status",
          "secs",
          "startedAt",
          "costN",
          "summary",
          "data",
          "evalPass",
          "recordingUrl",
          "transcript"
        ],
        "description": "One call with its transcript and per-turn latency."
      },
      "CallList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Call"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of Call records."
      },
      "CallVideo": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "heygen"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "lite",
              "full"
            ],
            "description": "lite: the assistant talks through the voice server; full: HeyGen runs speech, model and voice. Absent on older rows = lite."
          },
          "videoAgentId": {
            "type": "string"
          },
          "avatarId": {
            "type": "string"
          },
          "avatarName": {
            "type": "string"
          },
          "sessionId": {
            "type": "string"
          },
          "sandbox": {
            "type": "boolean"
          },
          "error": {
            "type": "string",
            "description": "Set when the avatar could not start or dropped and the call continued as audio."
          }
        },
        "required": [
          "provider",
          "videoAgentId",
          "avatarId",
          "avatarName"
        ],
        "description": "Video facts of a call made with a video agent."
      },
      "Campaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "orgId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "asst": {
            "type": "string",
            "description": "Name of the assistant that handles the calls."
          },
          "assistantId": {
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "fromNumber": {
            "type": "string",
            "description": "Caller ID (E.164)."
          },
          "startAt": {
            "type": "string",
            "description": "When a Scheduled campaign starts.",
            "format": "date-time"
          },
          "maxConcurrent": {
            "type": "integer"
          },
          "maxAttempts": {
            "type": "integer"
          },
          "retryDelayMin": {
            "type": "integer"
          },
          "callingWindow": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "start": {
                "type": "string",
                "description": "HH:MM"
              },
              "end": {
                "type": "string",
                "description": "HH:MM"
              },
              "days": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "description": "0 = Sunday … 6 = Saturday."
              }
            },
            "required": [
              "start",
              "end",
              "days"
            ],
            "description": "Calling hours (wall clock), within 08:00–21:00 unless windowOverride; null or absent = any time within 08:00–21:00 in the callee's zone."
          },
          "timeZoneSource": {
            "type": "string",
            "enum": [
              "contact",
              "campaign"
            ]
          },
          "timeZone": {
            "type": [
              "string",
              "null"
            ],
            "description": "IANA time zone."
          },
          "windowOverride": {
            "type": "boolean",
            "description": "Admin-set: dial outside the legal calling hours (08:00–21:00 in the callee's local time)."
          },
          "skippedOutsideHours": {
            "type": "integer"
          },
          "skippedDnc": {
            "type": "integer"
          },
          "nextWindowAt": {
            "type": "string",
            "format": "date-time"
          },
          "contactsScrubbedAt": {
            "type": "string",
            "description": "When the organization's contacts retention period masked the contact list (numbers masked, CSV variables removed); the campaign can no longer start.",
            "format": "date-time"
          },
          "done": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "Running",
              "Paused",
              "Scheduled",
              "Completed"
            ]
          },
          "when": {
            "type": "string",
            "description": "Display label of the schedule."
          },
          "contactCount": {
            "type": "integer",
            "description": "How many contacts the campaign has (the contacts themselves are not returned)."
          }
        },
        "required": [
          "id",
          "name",
          "asst",
          "from",
          "done",
          "total",
          "status",
          "when",
          "contactCount"
        ],
        "description": "An outbound campaign without its contacts."
      },
      "CampaignList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Campaign"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of Campaign records."
      },
      "Tool": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "orgId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "Function",
              "API request",
              "Transfer call",
              "End call",
              "Query",
              "SMS",
              "MCP server",
              "Google Calendar"
            ]
          },
          "desc": {
            "type": "string"
          },
          "used": {
            "type": "integer",
            "description": "Assistants using it."
          },
          "updated": {
            "type": "string",
            "description": "Display label of the last edit."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "JSON Schema of the function's arguments."
          },
          "url": {
            "type": "string"
          },
          "method": {
            "type": "string",
            "enum": [
              "POST",
              "GET"
            ]
          },
          "timeoutMs": {
            "type": "integer"
          },
          "async": {
            "type": "boolean",
            "description": "Fire-and-forget."
          },
          "requiresVerified": {
            "type": "boolean",
            "description": "Only for a verified caller: refused on calls whose caller has not passed the SMS code check (tools.verify)."
          },
          "headerCount": {
            "type": "integer",
            "description": "Request headers configured (their values are never returned)."
          }
        },
        "required": [
          "id",
          "name",
          "type",
          "desc",
          "used",
          "updated",
          "headerCount"
        ],
        "description": "A tool assistants can call. Request headers are left out."
      },
      "ToolList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tool"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of Tool records."
      },
      "PhoneNumber": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "orgId": {
            "type": "string"
          },
          "num": {
            "type": "string",
            "description": "The number as displayed."
          },
          "label": {
            "type": "string"
          },
          "asst": {
            "type": "string",
            "description": "Name of what answers it; empty = unassigned."
          },
          "targetId": {
            "type": "string"
          },
          "targetKind": {
            "type": "string",
            "enum": [
              "assistant",
              "workflow",
              "squad"
            ]
          },
          "prov": {
            "type": "string",
            "description": "Provider."
          },
          "sid": {
            "type": "string",
            "description": "Twilio IncomingPhoneNumber SID."
          },
          "e164": {
            "type": "string"
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "voice": {
                "type": "boolean"
              },
              "sms": {
                "type": "boolean"
              },
              "mms": {
                "type": "boolean"
              }
            }
          },
          "routing": {
            "type": "object",
            "properties": {
              "businessHours": {
                "type": "object",
                "properties": {
                  "timeZone": {
                    "type": "string"
                  },
                  "days": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "day": {
                          "type": "integer"
                        },
                        "open": {
                          "type": "string"
                        },
                        "close": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "day",
                        "open",
                        "close"
                      ]
                    }
                  }
                },
                "required": [
                  "timeZone",
                  "days"
                ]
              },
              "afterHours": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "assistant",
                      "voicemail",
                      "hangup"
                    ]
                  },
                  "targetId": {
                    "type": "string"
                  },
                  "targetKind": {
                    "type": "string",
                    "enum": [
                      "assistant",
                      "workflow",
                      "squad"
                    ]
                  },
                  "message": {
                    "type": "string"
                  }
                },
                "required": [
                  "action"
                ]
              },
              "holidays": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "YYYY-MM-DD"
                }
              }
            },
            "description": "Inbound routing by the clock; absent = always open."
          }
        },
        "required": [
          "id",
          "num",
          "label",
          "asst",
          "prov"
        ],
        "description": "A phone number and what answers it."
      },
      "PhoneNumberList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PhoneNumber"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of PhoneNumber records."
      },
      "KnowledgeFile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "orgId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "size": {
            "type": "string",
            "description": "Display size."
          },
          "kb": {
            "type": "string",
            "description": "Knowledge base name."
          },
          "status": {
            "type": "string",
            "enum": [
              "Ready",
              "Processing"
            ]
          },
          "added": {
            "type": "string"
          },
          "chars": {
            "type": "integer",
            "description": "Extracted text length."
          },
          "embedding": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "ready",
                  "pending",
                  "failed",
                  "none"
                ]
              },
              "model": {
                "type": "string"
              },
              "dims": {
                "type": "integer"
              },
              "chunks": {
                "type": "integer"
              },
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "error": {
                "type": "string"
              }
            },
            "required": [
              "status"
            ],
            "description": "Semantic-search state."
          },
          "sourceId": {
            "type": "string",
            "description": "Web source that crawled the page."
          },
          "sourceUrl": {
            "type": "string"
          },
          "contentHash": {
            "type": "string"
          },
          "crawledAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "size",
          "kb",
          "status",
          "added"
        ],
        "description": "A knowledge file (metadata only)."
      },
      "KnowledgeFileList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KnowledgeFile"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of KnowledgeFile records."
      },
      "KnowledgeBase": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "orgId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "fileCount": {
            "type": "integer",
            "description": "Files whose kb is this base's name."
          }
        },
        "required": [
          "id",
          "name",
          "createdAt",
          "fileCount"
        ],
        "description": "A knowledge base with its file count."
      },
      "KnowledgeBaseList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KnowledgeBase"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of KnowledgeBase records."
      },
      "DncEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "orgId": {
            "type": "string"
          },
          "number": {
            "type": "string",
            "description": "E.164.",
            "example": "+14155550100"
          },
          "reason": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "import",
              "api",
              "assistant",
              "sms"
            ]
          },
          "addedBy": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "at": {
            "type": "string",
            "description": "When it was added.",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "number",
          "source",
          "at"
        ],
        "description": "A number on the do-not-call list."
      },
      "DncEntryList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DncEntry"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of DncEntry records."
      },
      "DncCreate": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "A phone number; normalized to E.164."
          },
          "reason": {
            "type": "string",
            "description": "At most 200 characters."
          }
        },
        "required": [
          "number"
        ],
        "description": "A number to add to the do-not-call list."
      },
      "HookSubscription": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "hook_ + 24 hex characters; DELETE /api/v1/hooks/{id} unsubscribes."
          },
          "event": {
            "type": "string",
            "enum": [
              "call.ended",
              "booking.created",
              "voicemail.received",
              "campaign.finished",
              "lead.created",
              "callback.requested"
            ]
          },
          "url": {
            "type": "string",
            "description": "Where the events are POSTed (public https)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "event",
          "url",
          "createdAt"
        ],
        "description": "A REST hook subscription (Zapier, Make, n8n)."
      },
      "HookSubscriptionList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HookSubscription"
            }
          },
          "count": {
            "type": "integer",
            "description": "Items in this page."
          },
          "total": {
            "type": "integer",
            "description": "Items matching across all pages."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `?cursor=` for the next page; null on the last page. Opaque."
          }
        },
        "required": [
          "data",
          "count",
          "total",
          "hasMore",
          "nextCursor"
        ],
        "description": "One page of HookSubscription records."
      },
      "HookCreate": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "call.ended",
              "booking.created",
              "voicemail.received",
              "campaign.finished",
              "lead.created",
              "callback.requested"
            ],
            "description": "The event to receive."
          },
          "url": {
            "type": "string",
            "description": "Public https URL; checked again at every delivery.",
            "maxLength": 2048
          }
        },
        "required": [
          "event",
          "url"
        ],
        "description": "Subscribe a URL to one event.",
        "additionalProperties": false
      },
      "HookDeleteResult": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "integer",
            "description": "Subscriptions removed."
          }
        },
        "required": [
          "deleted"
        ]
      },
      "CampaignContactsAdd": {
        "type": "object",
        "properties": {
          "contacts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "phone": {
                  "type": "string",
                  "description": "A phone number (7+ digits, at most 32 characters)."
                },
                "vars": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  },
                  "description": "Values for the assistant's {{name}} placeholders."
                }
              },
              "required": [
                "phone"
              ]
            },
            "description": "At most 10,000 per request."
          }
        },
        "required": [
          "contacts"
        ],
        "description": "Contacts to append to a Paused or Scheduled campaign."
      },
      "CampaignContactsResult": {
        "type": "object",
        "properties": {
          "added": {
            "type": "integer"
          },
          "skipped": {
            "type": "integer",
            "description": "Invalid, not dialable under the dial policy, repeated or already on the campaign."
          },
          "total": {
            "type": "integer",
            "description": "Contacts on the campaign now."
          }
        },
        "required": [
          "added",
          "skipped",
          "total"
        ]
      },
      "CampaignStartResult": {
        "type": "object",
        "properties": {
          "campaign": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "Running",
                  "Paused",
                  "Scheduled",
                  "Completed"
                ]
              },
              "total": {
                "type": "integer"
              },
              "done": {
                "type": "integer"
              }
            },
            "required": [
              "id",
              "name",
              "status",
              "total",
              "done"
            ]
          },
          "simulated": {
            "type": "boolean",
            "description": "True for a sample campaign without contacts (no real calls)."
          }
        },
        "required": [
          "campaign",
          "simulated"
        ]
      },
      "Me": {
        "type": "object",
        "properties": {
          "key": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": "string",
                "description": "First characters of the key."
              },
              "created": {
                "type": "string",
                "description": "Display date the key was created."
              },
              "lastUsedAt": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "read",
                    "write"
                  ]
                },
                "description": "Effective scopes."
              }
            },
            "required": [
              "id",
              "name",
              "prefix",
              "created",
              "lastUsedAt",
              "scopes"
            ]
          },
          "org": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ]
          }
        },
        "required": [
          "key",
          "org"
        ],
        "description": "The key making the request and its organization."
      },
      "WebCallCreate": {
        "type": "object",
        "properties": {
          "assistantId": {
            "type": "string"
          },
          "customerId": {
            "type": "string",
            "description": "The caller's id in your own system: stored on the call and the key of returning-caller memory.",
            "pattern": "^[A-Za-z0-9._:@-]{1,128}$"
          },
          "variableValues": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Values for {{name}} placeholders: at most 20 keys, 2 KB. Supplied by your server, so they are trusted: caller verification (to: on_file) texts the number under phone / phone_number / mobile / … here."
          }
        },
        "required": [
          "assistantId"
        ],
        "description": "The assistant to call."
      },
      "WebCall": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Call token for the voice server (single use)."
          },
          "url": {
            "type": "string",
            "description": "Voice server WebSocket URL."
          },
          "expiresIn": {
            "type": "integer",
            "description": "Seconds the token is valid (300)."
          },
          "assistantId": {
            "type": "string"
          },
          "test": {
            "type": "boolean",
            "description": "Started with a test key: a free test call."
          }
        },
        "required": [
          "token",
          "url",
          "expiresIn",
          "assistantId"
        ],
        "description": "A browser call ready to connect."
      },
      "InlineAssistant": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Up to 80 characters. Required on create.",
            "maxLength": 80
          },
          "firstMessage": {
            "type": "string",
            "description": "What the assistant says first.",
            "maxLength": 2000
          },
          "prompt": {
            "type": "string",
            "description": "System prompt.",
            "maxLength": 20000
          },
          "provider": {
            "type": "string",
            "enum": [
              "OpenAI",
              "Anthropic",
              "Google"
            ],
            "description": "Model provider."
          },
          "model": {
            "type": "string",
            "description": "Model id, e.g. gpt-4o. On Voxa's model keys only the priced models are accepted (gpt-4o, gpt-4o-mini, gpt-4.1, claude-sonnet-4, claude-haiku-4, gemini-2.5-flash, gemini-2.5-pro and a few more; a 400 on `model` lists them); with your own key for the provider any model id.",
            "maxLength": 80
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2
          },
          "maxTokens": {
            "type": "integer",
            "description": "Stored; not applied to live calls.",
            "minimum": 1,
            "maximum": 32000
          },
          "voiceProvider": {
            "type": "string",
            "enum": [
              "Cartesia",
              "ElevenLabs",
              "Deepgram",
              "OpenAI"
            ]
          },
          "voice": {
            "type": "string",
            "description": "Voice name.",
            "maxLength": 80
          },
          "voiceId": {
            "type": "string",
            "description": "Provider voice id used instead of the mapped one (non-Deepgram voices).",
            "maxLength": 200
          },
          "language": {
            "type": "string",
            "description": "BCP-47 language, e.g. en-US.",
            "maxLength": 20
          },
          "tProvider": {
            "type": "string",
            "enum": [
              "Deepgram",
              "AssemblyAI",
              "Google",
              "OpenAI"
            ],
            "description": "Transcriber provider."
          },
          "tModel": {
            "type": "string",
            "description": "Transcriber model. Deepgram: nova-3, nova-3-multi, flux-general-en, flux-general-multi or nova-2; a create without it gets the deployment's default (nova-3 unless configured); calls fall back to a model that covers the language.",
            "maxLength": 80
          },
          "sttEndOfTurn": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "threshold": {
                "type": "number",
                "description": "End-of-turn confidence (Deepgram's default 0.7): higher waits for clearer pauses, lower answers sooner.",
                "minimum": 0.5,
                "maximum": 1
              },
              "eager": {
                "type": "number",
                "description": "Eager end-of-turn confidence: the agent starts preparing a reply early. At most `threshold` (0.7 when unset); absent = off.",
                "minimum": 0.3,
                "maximum": 0.9
              },
              "timeoutMs": {
                "type": "integer",
                "description": "End the turn after this much silence, in milliseconds (default 5000).",
                "minimum": 500,
                "maximum": 10000
              }
            },
            "description": "Flux end-of-turn detection (tModel flux-general-en or flux-general-multi; other models ignore it). Absent fields use the defaults; null or {} clears. Errors name the setting (`sttEndOfTurn.eager`).",
            "additionalProperties": false
          },
          "sttKeyterms": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Keyterm prompting (Nova-3 and Flux): product names, jargon and proper nouns to recognise, one word or phrase per item. Trimmed and de-duplicated; no control characters; null clears (stored as []).",
            "maxItems": 100
          },
          "endPhrases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Phrases that end the call.",
            "maxItems": 50
          },
          "maxDuration": {
            "type": "integer",
            "description": "Maximum call length in minutes.",
            "minimum": 1,
            "maximum": 180
          },
          "silenceTimeout": {
            "type": "integer",
            "description": "Seconds of silence before hanging up.",
            "minimum": 1,
            "maximum": 600
          },
          "recording": {
            "type": "boolean",
            "description": "Record calls."
          },
          "hipaa": {
            "type": "boolean",
            "description": "No transcript, recording or analysis leaves the call."
          },
          "kb": {
            "type": "string",
            "description": "Knowledge base name.",
            "maxLength": 120
          },
          "tools": {
            "type": "object",
            "properties": {
              "book": {
                "type": "boolean"
              },
              "transfer": {
                "type": "boolean"
              },
              "end": {
                "type": "boolean"
              },
              "order": {
                "type": "boolean"
              },
              "query": {
                "type": "boolean"
              },
              "dnc": {
                "type": "boolean"
              },
              "verify": {
                "type": "boolean"
              },
              "crm": {
                "type": "boolean"
              },
              "square": {
                "type": "boolean"
              },
              "jobber": {
                "type": "boolean"
              },
              "nexhealth": {
                "type": "boolean"
              }
            },
            "description": "Built-in tool switches. On writes a partial object is merged with the current value. `dnc` (add the caller to the do-not-call list) and `verify` (verify the caller by an SMS code through the organization's own Twilio) may be missing on older assistants (off).",
            "additionalProperties": false
          },
          "toolIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tool records the assistant may call.",
            "maxItems": 50
          },
          "transferNumber": {
            "type": "string",
            "description": "E.164 destination of the built-in transfer tool. A new value must pass the dial policy (no premium-rate prefixes; a country the organization may dial), else 400 on the field.",
            "maxLength": 20
          },
          "summaryPrompt": {
            "type": "string",
            "description": "Post-call summary instructions.",
            "maxLength": 2000
          },
          "evaluation": {
            "type": "string",
            "enum": [
              "pass",
              "numeric",
              "descriptive"
            ],
            "description": "Success evaluation type of the post-call analysis."
          },
          "analysisSchema": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Field name in the extracted data."
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "number",
                    "boolean",
                    "enum"
                  ]
                },
                "description": {
                  "type": "string",
                  "description": "What the model should extract."
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Allowed values when `type` is enum."
                }
              },
              "required": [
                "key",
                "type"
              ],
              "description": "One structured-data field of the post-call analysis.",
              "additionalProperties": false
            },
            "description": "Structured data the post-call analysis extracts.",
            "maxItems": 50
          }
        },
        "description": "An inline assistant configuration (web SDK). Needs the organization's allowInlineAssistants switch; kept as a hidden Published assistant. Limited by the organization's inline assistant policy (API keys page): by default no toolIds, no kb, the default model only, no built-in tools besides end and query, and a prompt of at most 4000 characters; anything else is a 400 naming the field.",
        "additionalProperties": false
      },
      "VoiceTokenRequest": {
        "type": "object",
        "properties": {
          "publicKey": {
            "type": "string",
            "description": "The organization's public key (pk_...).",
            "example": "pk_..."
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant to talk to (Published)."
          },
          "assistant": {
            "$ref": "#/components/schemas/InlineAssistant"
          },
          "variableValues": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Caller details for this call: at most 20 keys, 2 KB. With a public key they are unverified: cleaned to one line each and shown to the assistant only as a delimited caller-supplied block after the prompt; they fill {{name}} placeholders and workflow variables only for names the organization allow-lists (inlineAssistantPolicy.publicVariables), and caller verification never texts a phone number from them (mint the token with POST /api/v1/web-calls for trusted values). From a console session they fill placeholders directly."
          },
          "workflowId": {
            "type": "string",
            "description": "Run this workflow."
          },
          "squadId": {
            "type": "string",
            "description": "Run this squad."
          },
          "videoAgentId": {
            "type": "string",
            "description": "Lite-mode video agent: its assistant answers and the avatar is shown."
          },
          "via": {
            "type": "string",
            "enum": [
              "sdk",
              "widget"
            ],
            "description": "sdk follows the widget's rules and is logged as sdk."
          },
          "customerId": {
            "type": "string",
            "description": "The caller's id in your own system: stored on the call and the key of returning-caller memory. Used only with a valid customerIdSignature; otherwise ignored (see customerIdIgnored).",
            "pattern": "^[A-Za-z0-9._:@-]{1,128}$"
          },
          "customerIdSignature": {
            "type": "string",
            "description": "Your server's signature of customerId: `<expires>.<hex>`, a Unix time in seconds (future, at most 7 days ahead) and the lowercase hex HMAC-SHA256 of `${customerId}.${expires}` keyed with the organization's customer id signing secret (Organization settings).",
            "pattern": "^[0-9]{1,12}\\.[0-9a-f]{64}$"
          }
        },
        "required": [
          "publicKey"
        ],
        "description": "One of assistantId, assistant, workflowId, squadId or videoAgentId says who answers."
      },
      "VoiceToken": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Call token for the voice server (single use)."
          },
          "url": {
            "type": "string",
            "description": "Voice server WebSocket URL."
          },
          "assistantId": {
            "type": "string"
          },
          "assistantName": {
            "type": "string"
          },
          "workflowId": {
            "type": "string"
          },
          "squadId": {
            "type": "string"
          },
          "videoAgentId": {
            "type": "string"
          },
          "video": {
            "type": "object",
            "properties": {
              "avatarName": {
                "type": "string"
              },
              "sandbox": {
                "type": "boolean"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "lite"
                ]
              }
            },
            "required": [
              "avatarName",
              "sandbox",
              "mode"
            ]
          },
          "expiresIn": {
            "type": "integer",
            "description": "Seconds the token is valid (300)."
          },
          "test": {
            "type": "boolean",
            "description": "Issued for the test public key: a free test call."
          },
          "customerIdIgnored": {
            "type": "string",
            "enum": [
              "unsigned",
              "invalid_signature",
              "expired",
              "expires_too_far"
            ],
            "description": "The customerId was dropped (no valid customerIdSignature): the call runs without returning-caller memory."
          }
        },
        "required": [
          "token",
          "url",
          "assistantId",
          "assistantName",
          "expiresIn"
        ],
        "description": "A call token."
      },
      "VideoAgentPublic": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "lite",
              "full"
            ],
            "description": "Which start route to use: lite → POST /api/voice/token, full → POST /api/video/sessions."
          },
          "sandbox": {
            "type": "boolean"
          },
          "avatarName": {
            "type": "string"
          },
          "avatarPreviewUrl": {
            "type": "string"
          },
          "interactivity": {
            "type": "string",
            "enum": [
              "conversational",
              "push_to_talk"
            ],
            "description": "Full mode only."
          },
          "status": {
            "type": "string",
            "enum": [
              "Published"
            ]
          },
          "test": {
            "type": "boolean",
            "description": "Asked with the test public key (Lite agents then report sandbox true)."
          }
        },
        "required": [
          "id",
          "name",
          "mode",
          "sandbox",
          "avatarName",
          "status"
        ],
        "description": "What a site may know about a Published video agent."
      },
      "ChatMessageRequest": {
        "type": "object",
        "properties": {
          "publicKey": {
            "type": "string",
            "description": "The organization's public key (pk_...); test keys are refused."
          },
          "assistantId": {
            "type": "string",
            "description": "A Published assistant: starts a chat (without `session`)."
          },
          "session": {
            "type": "string",
            "description": "The `session` of the chat's previous answer: the next message of that chat."
          },
          "text": {
            "type": "string",
            "description": "The visitor's message, 1 to 2000 characters. Optional when starting (the answer then has the greeting only)."
          },
          "customerId": {
            "type": "string",
            "description": "The visitor's id in your own system; counts only with customerIdSignature (keys returning-caller memory).",
            "pattern": "^[A-Za-z0-9._:@-]{1,128}$"
          },
          "customerIdSignature": {
            "type": "string",
            "description": "<expires>.<hex> from your server, as for calls."
          }
        },
        "required": [
          "publicKey"
        ]
      },
      "ChatMessages": {
        "type": "object",
        "properties": {
          "session": {
            "type": "string",
            "description": "Send it with the next message (renewed with every answer, valid 24 hours)."
          },
          "conversationId": {
            "type": "string",
            "description": "The conversation (the console's Conversations page)."
          },
          "assistantId": {
            "type": "string"
          },
          "assistantName": {
            "type": "string"
          },
          "messages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "type": "string",
                  "enum": [
                    "assistant"
                  ]
                },
                "text": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "description": "ISO 8601."
                }
              },
              "required": [
                "role",
                "text",
                "at"
              ]
            },
            "description": "The assistant's new messages: the greeting when a chat starts, then the reply."
          }
        },
        "required": [
          "session",
          "conversationId",
          "assistantId",
          "assistantName",
          "messages"
        ]
      },
      "VideoSessionRequest": {
        "type": "object",
        "properties": {
          "publicKey": {
            "type": "string",
            "description": "The organization's public key (pk_...)."
          },
          "videoAgentId": {
            "type": "string",
            "description": "A Published Full-mode video agent."
          },
          "via": {
            "type": "string",
            "enum": [
              "sdk",
              "widget"
            ]
          }
        },
        "required": [
          "publicKey",
          "videoAgentId"
        ]
      },
      "FullVideoSession": {
        "type": "object",
        "properties": {
          "callId": {
            "type": "string"
          },
          "sessionId": {
            "type": "string"
          },
          "sessionToken": {
            "type": "string",
            "description": "Start the session with HeyGen's web SDK: new LiveAvatarSession(sessionToken)."
          },
          "avatarName": {
            "type": "string"
          },
          "sandbox": {
            "type": "boolean"
          },
          "maxSec": {
            "type": "integer",
            "description": "Session limit in seconds (what HeyGen granted); the billed duration never exceeds it by more than 5 s."
          },
          "interactivity": {
            "type": "string",
            "enum": [
              "conversational",
              "push_to_talk"
            ]
          },
          "openingText": {
            "type": "string"
          },
          "ratePerMinute": {
            "type": "number",
            "description": "Dollars per minute."
          },
          "auth": {
            "type": "string",
            "description": "Send as header x-voxa-session-auth to /alive and /end."
          },
          "mode": {
            "type": "string",
            "enum": [
              "full"
            ]
          },
          "test": {
            "type": "boolean",
            "description": "Started with the test public key: a free test call."
          }
        },
        "required": [
          "callId",
          "sessionId",
          "sessionToken",
          "avatarName",
          "sandbox",
          "interactivity",
          "openingText",
          "ratePerMinute",
          "auth",
          "mode"
        ],
        "description": "A started Full-mode video session."
      },
      "VideoSessionEnd": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "enum": [
              "user",
              "assistant",
              "stopped",
              "error"
            ],
            "description": "Who ended the session."
          },
          "stopReason": {
            "type": "string",
            "description": "HeyGen's stop reason as the web SDK reports it (MAX_DURATION_REACHED, IDLE_TIMEOUT...)."
          },
          "transcript": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "type": "string",
                  "enum": [
                    "user",
                    "assistant"
                  ]
                },
                "text": {
                  "type": "string"
                }
              },
              "required": [
                "role",
                "text"
              ]
            },
            "description": "Fallback when HeyGen has no transcript: at most 300 lines of 1000 characters (64 000 in all); stored with transcriptSource \"client\"."
          },
          "maxSec": {
            "type": "integer",
            "description": "Ignored (kept for older SDKs): the billed duration is the server's own, capped at the limit stored at start."
          },
          "auth": {
            "type": "string",
            "description": "The start's auth, when it cannot travel as a header (navigator.sendBeacon)."
          }
        }
      },
      "VideoSessionEnded": {
        "type": "object",
        "properties": {
          "call": {
            "type": "object",
            "additionalProperties": true,
            "description": "The finished call record."
          }
        },
        "required": [
          "call"
        ]
      },
      "SuiteRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The run's job id."
          },
          "at": {
            "type": "string",
            "description": "When the run finished.",
            "format": "date-time"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "manual",
              "publish",
              "api"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "done",
              "failed"
            ]
          },
          "passed": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "passRate": {
            "type": "number",
            "description": "passed / total, 0..1."
          },
          "previousPassRate": {
            "type": "number"
          },
          "regressed": {
            "type": "boolean"
          },
          "fingerprint": {
            "type": "string",
            "description": "The assistant's configuration at run start."
          },
          "assistantId": {
            "type": "string"
          },
          "charged": {
            "type": "number",
            "description": "Dollars."
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "at",
          "trigger",
          "status",
          "passed",
          "failed",
          "total",
          "passRate"
        ],
        "description": "One finished test suite run."
      },
      "EraseRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "The caller's phone number, any common format (normalized to E.164)."
          },
          "customerId": {
            "type": "string",
            "description": "The caller's id in your own system (the web SDK / widget customerId).",
            "pattern": "^[A-Za-z0-9._:@-]{1,128}$"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Only count what would be erased (default false)."
          },
          "cancelBookings": {
            "type": "boolean",
            "description": "Also delete (Google Calendar) or cancel (Cal.com) the calendar bookings the erased calls made, through the connected integration (default false; best effort)."
          }
        },
        "description": "Who to erase: a number, a customer id, or both (at least one).",
        "additionalProperties": false
      },
      "EraseResult": {
        "type": "object",
        "properties": {
          "dryRun": {
            "type": "boolean"
          },
          "calls": {
            "type": "integer",
            "description": "Calls scrubbed (transcript, summary, caller data, recording; customer \"[erased]\"); billing facts kept."
          },
          "recordings": {
            "type": "integer",
            "description": "Recordings deleted."
          },
          "contacts": {
            "type": "integer",
            "description": "Returning-caller contacts deleted."
          },
          "campaignContacts": {
            "type": "integer",
            "description": "Campaign contacts erased (number \"[erased]\", CSV variables removed, never dialed again) or whose CSV variables naming the caller were redacted."
          },
          "auditEntries": {
            "type": "integer",
            "description": "Activity log entries whose mentions of the caller (summary, target, values) were replaced with \"[erased]\"."
          },
          "logs": {
            "type": "integer",
            "description": "Log entries redacted."
          },
          "webhookPayloads": {
            "type": "integer",
            "description": "Stored webhook deliveries (retries, dead letters) whose payload named the caller or an erased call: deleted, so they cannot be replayed."
          },
          "twilioRecordings": {
            "type": "integer",
            "description": "Recordings deleted from Twilio (voicemails Twilio still held)."
          },
          "bookingsCancelled": {
            "type": "integer",
            "description": "Calendar bookings deleted or cancelled (only with cancelBookings)."
          },
          "bookingsFailed": {
            "type": "integer",
            "description": "Calendar bookings that could not be cancelled (integration not connected, provider error)."
          },
          "dncKept": {
            "type": "integer",
            "description": "Do-not-call entries of the number: kept on purpose (the legal basis for never calling it again)."
          },
          "skippedLive": {
            "type": "integer",
            "description": "Matching calls still running, and campaign contacts whose dial is in flight: not touched; erase again once they ended."
          },
          "failed": {
            "type": "integer",
            "description": "Calls whose recording could not be deleted (storage error): unchanged; erase again to retry."
          },
          "subject": {
            "type": "string",
            "description": "Hash prefix of the subject as recorded in the activity log (never the number or id)."
          }
        },
        "required": [
          "dryRun",
          "calls",
          "recordings",
          "contacts",
          "campaignContacts",
          "auditEntries",
          "logs",
          "webhookPayloads",
          "twilioRecordings",
          "bookingsCancelled",
          "bookingsFailed",
          "dncKept",
          "skippedLive",
          "failed",
          "subject"
        ]
      },
      "WebhookStatusUpdate": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "status-update"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "status": {
            "type": "string",
            "enum": [
              "in-progress"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "Inbound",
              "Outbound",
              "Web"
            ]
          },
          "customer": {
            "type": "string",
            "description": "The caller's number or label (redacted under HIPAA)."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "status",
          "type",
          "customer"
        ],
        "description": "A call connected."
      },
      "WebhookTranscript": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "transcript"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "who": {
                  "type": "string",
                  "enum": [
                    "Assistant",
                    "Caller"
                  ]
                },
                "text": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "description": "When the line was heard.",
                  "format": "date-time"
                }
              },
              "required": [
                "who",
                "text",
                "at"
              ]
            }
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "lines"
        ],
        "description": "New transcript lines."
      },
      "WebhookToolCalls": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "tool-calls"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "name": {
            "type": "string",
            "description": "Tool (function) name."
          },
          "arguments": {
            "type": "object",
            "additionalProperties": true,
            "description": "The arguments the model passed."
          },
          "result": {
            "type": "string",
            "description": "What the tool answered."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "name",
          "arguments",
          "result"
        ],
        "description": "The assistant called a tool."
      },
      "WebhookEndOfCallReport": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "end-of-call-report"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "call": {
            "$ref": "#/components/schemas/CallDetail"
          },
          "videoAgentId": {
            "type": "string",
            "description": "Full-mode video calls: the video agent."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "call"
        ],
        "description": "A call ended and was saved."
      },
      "WebhookAnalysis": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "analysis"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "videoAgentId": {
            "type": "string",
            "description": "Full-mode video calls: the video agent."
          },
          "summary": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Keyed by the analysis schema's keys; null for a value the model could not find."
          },
          "evaluation": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "pass",
                  "numeric",
                  "descriptive"
                ]
              },
              "pass": {
                "type": "boolean"
              },
              "score": {
                "type": "number"
              },
              "text": {
                "type": "string"
              }
            },
            "required": [
              "type",
              "pass"
            ]
          },
          "analyzedAt": {
            "type": "string",
            "format": "date-time"
          },
          "score": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer",
                "description": "Weighted 0..100."
              },
              "criteria": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "score": {
                      "type": "integer",
                      "description": "0..100."
                    },
                    "pass": {
                      "type": "boolean"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "score",
                    "pass"
                  ]
                }
              }
            },
            "required": [
              "total",
              "criteria"
            ],
            "description": "Present when a scorecard applies to the call (the assistant's, else the organization's default)."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "summary",
          "data",
          "evaluation",
          "analyzedAt"
        ],
        "description": "The post-call analysis finished."
      },
      "WebhookCallFlagged": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "call.flagged"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "low_score",
                "negative_sentiment",
                "keyword"
              ]
            }
          },
          "score": {
            "type": "integer",
            "description": "Total scorecard score 0..100, when scored."
          },
          "sentiment": {
            "type": "number",
            "description": "The caller's sentiment, -1..1."
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The watched words that were said."
          },
          "excerpts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Up to three matching lines, \"Caller: ...\" / \"Assistant: ...\"."
          },
          "hipaa": {
            "type": "boolean",
            "description": "Present (true) for HIPAA calls."
          },
          "flaggedAt": {
            "type": "string",
            "format": "date-time"
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "reasons",
          "flaggedAt"
        ],
        "description": "A call tripped a quality alert."
      },
      "WebhookBillingThreshold": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "billing.threshold"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "threshold": {
            "type": "number",
            "description": "Percent of the monthly limit."
          },
          "monthUsed": {
            "type": "number",
            "description": "Dollars."
          },
          "monthlyLimit": {
            "type": "number",
            "description": "Dollars."
          },
          "month": {
            "type": "string",
            "description": "YYYY-MM"
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "threshold",
          "monthUsed",
          "monthlyLimit",
          "month"
        ],
        "description": "A usage alert threshold was reached."
      },
      "WebhookSuiteFinished": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "suite.finished"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "suiteId": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "description": "The suite's name."
          },
          "run": {
            "$ref": "#/components/schemas/SuiteRun"
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "suiteId",
          "name",
          "run"
        ],
        "description": "A test suite run finished."
      },
      "WebhookVoicemailReceived": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "voicemail.received"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "from": {
            "type": "string",
            "description": "The caller."
          },
          "to": {
            "type": "string",
            "description": "Your number (E.164)."
          },
          "durationSec": {
            "type": "integer"
          },
          "transcript": {
            "type": [
              "string",
              "null"
            ]
          },
          "recordingUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "/api/v1/calls/{id}/recording"
          },
          "hipaa": {
            "type": "boolean",
            "description": "Present (true) when the voicemail fell under HIPAA mode: nothing of the message was kept."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "from",
          "to",
          "durationSec",
          "transcript",
          "recordingUrl"
        ],
        "description": "A caller left a voicemail."
      },
      "WebhookCallbackRequested": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "callback.requested"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callback": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The callback."
              },
              "callId": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The call it was requested on."
              },
              "number": {
                "type": "string",
                "description": "The caller's number (E.164), or \"[redacted]\"."
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "One line, masked with the call's PII redaction."
              },
              "preferredAt": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "ISO 8601."
              },
              "timeZone": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "IANA zone the time was judged in."
              },
              "status": {
                "type": "string",
                "enum": [
                  "open",
                  "done",
                  "cancelled"
                ]
              },
              "source": {
                "type": "string",
                "enum": [
                  "assistant",
                  "transfer"
                ]
              },
              "hipaa": {
                "type": "boolean",
                "description": "Present (true) on HIPAA calls."
              }
            },
            "required": [
              "id",
              "number",
              "status",
              "source"
            ]
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callback"
        ],
        "description": "A caller asked to be called back."
      },
      "WebhookReportReady": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "report.ready"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "reportId": {
            "type": "string",
            "description": "The stored report."
          },
          "period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "description": "Start (inclusive), local midnight in timeZone.",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "description": "End (exclusive).",
                "format": "date-time"
              },
              "timeZone": {
                "type": "string",
                "description": "IANA time zone of the schedule."
              },
              "kind": {
                "type": "string",
                "enum": [
                  "daily",
                  "weekly",
                  "custom"
                ]
              },
              "label": {
                "type": "string",
                "description": "e.g. Oct 2 – Oct 8, 2026"
              }
            },
            "required": [
              "from",
              "to",
              "timeZone",
              "kind",
              "label"
            ]
          },
          "summary": {
            "type": "object",
            "properties": {
              "calls": {
                "type": "integer",
                "description": "Finished, non-test calls that started in the period."
              },
              "minutes": {
                "type": "number"
              },
              "cost": {
                "type": "number",
                "description": "Dollars."
              },
              "passRate": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Share of evaluated calls that passed, 0..1."
              }
            },
            "required": [
              "calls",
              "minutes",
              "cost",
              "passRate"
            ]
          },
          "roi": {
            "type": "object",
            "additionalProperties": true,
            "description": "Present when the organization set its ROI values (Overview, Return on investment): { settings, current, previous, pct } with handled calls, hours saved, bookings, leads, estimated value (settings.currency), Voxa charges (USD), net and the return multiple (USD values only)."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "reportId",
          "period",
          "summary"
        ],
        "description": "A scheduled report was built."
      },
      "WebhookPaymentSucceeded": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "payment.succeeded"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "payment": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The payment request (pay_…; also metadata.voxaPaymentId on the Stripe Checkout Session)."
              },
              "amount": {
                "type": "integer",
                "description": "Minor units (cents)."
              },
              "currency": {
                "type": "string",
                "description": "Lower-case ISO 4217 code."
              },
              "status": {
                "type": "string",
                "enum": [
                  "paid",
                  "failed",
                  "expired"
                ]
              },
              "method": {
                "type": "string",
                "enum": [
                  "link",
                  "keypad"
                ]
              },
              "description": {
                "type": "string"
              },
              "reason": {
                "type": "string",
                "description": "Why it failed or expired (failed / expired), e.g. expired, payment_failed, too-many-failed-attempts, cancelled."
              },
              "confirmation": {
                "type": "string",
                "description": "Stripe payment intent id, or the Pay connector's confirmation code (paid)."
              },
              "testMode": {
                "type": "boolean",
                "description": "Made with a Stripe test-mode key."
              },
              "at": {
                "type": "string",
                "description": "When the status changed.",
                "format": "date-time"
              }
            },
            "required": [
              "id",
              "amount",
              "currency",
              "status",
              "method",
              "at"
            ]
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "payment"
        ],
        "description": "A payment during a call was paid."
      },
      "WebhookPaymentFailed": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "payment.failed"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "payment": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The payment request (pay_…; also metadata.voxaPaymentId on the Stripe Checkout Session)."
              },
              "amount": {
                "type": "integer",
                "description": "Minor units (cents)."
              },
              "currency": {
                "type": "string",
                "description": "Lower-case ISO 4217 code."
              },
              "status": {
                "type": "string",
                "enum": [
                  "paid",
                  "failed",
                  "expired"
                ]
              },
              "method": {
                "type": "string",
                "enum": [
                  "link",
                  "keypad"
                ]
              },
              "description": {
                "type": "string"
              },
              "reason": {
                "type": "string",
                "description": "Why it failed or expired (failed / expired), e.g. expired, payment_failed, too-many-failed-attempts, cancelled."
              },
              "confirmation": {
                "type": "string",
                "description": "Stripe payment intent id, or the Pay connector's confirmation code (paid)."
              },
              "testMode": {
                "type": "boolean",
                "description": "Made with a Stripe test-mode key."
              },
              "at": {
                "type": "string",
                "description": "When the status changed.",
                "format": "date-time"
              }
            },
            "required": [
              "id",
              "amount",
              "currency",
              "status",
              "method",
              "at"
            ]
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "payment"
        ],
        "description": "A payment during a call failed or expired."
      },
      "WebhookSmsTextBack": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "sms.text_back"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "numberId": {
            "type": "string",
            "description": "Your phone number's id."
          },
          "from": {
            "type": "string",
            "description": "Your number (E.164)."
          },
          "to": {
            "type": "string",
            "description": "The caller (E.164)."
          },
          "trigger": {
            "type": "string",
            "enum": [
              "abandoned",
              "unanswered",
              "after_hours"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "failed"
            ]
          },
          "sid": {
            "type": "string",
            "description": "Twilio Message SID (sent)."
          },
          "sentAt": {
            "type": "string",
            "description": "When the message was handed to Twilio (sent).",
            "format": "date-time"
          },
          "error": {
            "type": "string",
            "description": "Why the send failed (failed)."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "numberId",
          "from",
          "to",
          "trigger",
          "status"
        ],
        "description": "A missed caller was texted back."
      },
      "WebhookConversationStarted": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "conversation.started"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "conversationId": {
            "type": "string",
            "description": "The conversation."
          },
          "channel": {
            "type": "string",
            "enum": [
              "sms",
              "whatsapp",
              "chat"
            ]
          },
          "participant": {
            "type": "string",
            "description": "SMS / WhatsApp: the person's number (E.164); chat: the signed customer id, or \"\"."
          },
          "numberId": {
            "type": "string",
            "description": "SMS / WhatsApp: your number's id."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "conversationId",
          "channel",
          "participant",
          "startedAt"
        ],
        "description": "A text conversation started."
      },
      "WebhookConversationEnded": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "conversation.ended"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "conversationId": {
            "type": "string",
            "description": "The conversation."
          },
          "channel": {
            "type": "string",
            "enum": [
              "sms",
              "whatsapp",
              "chat"
            ]
          },
          "participant": {
            "type": "string"
          },
          "numberId": {
            "type": "string"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "closedAt": {
            "type": "string",
            "format": "date-time"
          },
          "reason": {
            "type": "string",
            "enum": [
              "idle",
              "opt_out",
              "manual",
              "limit"
            ]
          },
          "messages": {
            "type": "integer",
            "description": "Messages in the conversation (both sides)."
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Schema key → extracted value (the assistant's structured data schema)."
          },
          "evaluation": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "{ type, pass, score?, text? }"
          },
          "cost": {
            "type": "number",
            "description": "Dollars charged for the conversation."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "conversationId",
          "channel",
          "participant",
          "startedAt",
          "closedAt",
          "reason",
          "messages"
        ],
        "description": "A text conversation ended."
      },
      "WebhookCallEnded": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "call.ended"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "videoAgentId": {
            "type": "string",
            "description": "Full-mode video calls: the video agent."
          },
          "assistantName": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "Inbound",
              "Outbound",
              "Web",
              null
            ]
          },
          "customer": {
            "type": [
              "string",
              "null"
            ],
            "description": "The caller's number or label."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "live, interrupted, completed, failed, transferred or voicemail."
          },
          "endedReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "durationSeconds": {
            "type": [
              "number",
              "null"
            ]
          },
          "summary": {
            "type": "string",
            "description": "The call's summary at the end (the analysis may refine it later: subscribe to the analysis webhook for that)."
          },
          "recordingUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "GET it with an API key."
          },
          "consoleUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The call in the console (needs PUBLIC_APP_URL)."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "summary"
        ],
        "description": "A call ended (automation hook)."
      },
      "WebhookBookingCreated": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "booking.created"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "booking": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "google_calendar",
                  "calcom"
                ]
              },
              "integrationId": {
                "type": "string"
              },
              "eventId": {
                "type": "string"
              },
              "start": {
                "type": "string",
                "format": "date-time"
              },
              "end": {
                "type": "string",
                "format": "date-time"
              },
              "title": {
                "type": "string"
              },
              "timeZone": {
                "type": "string"
              },
              "link": {
                "type": "string"
              }
            },
            "required": [
              "provider",
              "eventId",
              "start",
              "end",
              "title"
            ]
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "booking"
        ],
        "description": "An appointment was booked (automation hook)."
      },
      "WebhookCampaignFinished": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "campaign.finished"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "campaignId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "total": {
            "type": "integer",
            "description": "Contacts on the campaign."
          },
          "dialed": {
            "type": "integer"
          },
          "outcomes": {
            "type": "object",
            "additionalProperties": true,
            "description": "Outcome → count (completed, no-answer, busy, failed, voicemail, dnc, not_dialed...)."
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time"
          },
          "simulated": {
            "type": "boolean",
            "description": "A sample campaign without contacts."
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "campaignId",
          "name",
          "total",
          "dialed",
          "outcomes",
          "finishedAt"
        ],
        "description": "A campaign finished (automation hook)."
      },
      "WebhookLeadCreated": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "lead.created"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "When the event was sent.",
            "format": "date-time"
          },
          "assistantId": {
            "type": "string",
            "description": "The assistant the event is about (absent when there is none)."
          },
          "callId": {
            "type": "string",
            "description": "The call."
          },
          "provider": {
            "type": "string",
            "enum": [
              "hubspot",
              "salesforce",
              "pipedrive"
            ]
          },
          "recordId": {
            "type": "string",
            "description": "HubSpot contact id, Salesforce Lead id or Pipedrive person id."
          },
          "recordType": {
            "type": "string",
            "enum": [
              "contact",
              "lead",
              "person"
            ]
          },
          "leadId": {
            "type": "string",
            "description": "Pipedrive: the lead created next to the person."
          },
          "via": {
            "type": "string",
            "enum": [
              "post-call",
              "tool"
            ]
          },
          "test": {
            "type": "boolean",
            "description": "Present (true) on events of test calls, which go only to the organization's test webhook URL."
          }
        },
        "required": [
          "event",
          "timestamp",
          "callId",
          "provider",
          "recordId",
          "recordType",
          "via"
        ],
        "description": "A lead was created in the CRM (automation hook)."
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A secret API key (sk_live_…). Scopes: read (GET, HEAD), write (POST, PATCH, DELETE)."
      },
      "sessionAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-voxa-session-auth",
        "description": "The `auth` POST /api/video/sessions answered; may travel as `auth` in the JSON body instead (navigator.sendBeacon)."
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Requests per minute the key may make.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "WWW-Authenticate": {
        "description": "Bearer realm=\"voxa\"",
        "schema": {
          "type": "string"
        }
      },
      "Location": {
        "description": "Where to go next (a signed URL valid for a limited time).",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, unknown or revoked API key.",
        "headers": {
          "WWW-Authenticate": {
            "$ref": "#/components/headers/WWW-Authenticate"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Invalid API key"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key lacks the scope this method needs, or the request comes from outside the key's IP allowlist (reason ip_not_allowed, no rate-limit headers).",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "This API key does not have the \"write\" scope"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded; wait Retry-After seconds.",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Rate limit of 120 requests per minute exceeded"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error; it has been logged.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Internal error"
            }
          }
        }
      }
    }
  }
}
