{
  "openapi": "3.0.1",
  "info": {
    "title": "Envocal API",
    "description": "Provision and report on the Envocal voice AI platform: tenants and members, agents, knowledge bases and\r\nphone numbers, and read conversations and usage. More (tools, credentials, widget configurations, agent\r\nlookup by phone number) follows in later additions to v1.\r\n\r\n## Base URL and authentication\r\nEvery path starts with `/v1`: `https://api.envocal.ai/v1/...`. Send a tenant API key as a bearer token on\r\nevery request:\r\n\r\n    Authorization: Bearer evk_eu_...\r\n\r\nTenant admins create and revoke keys in **Studio → Vault → API keys**. The full key is shown once; store it\r\nsecurely (e.g. in a secrets manager). The second part of the key (`eu`) is the data region the key and its\r\ntenant live in. Revoked or expired keys get `401 Unauthorized`.\r\n\r\n## Permissions\r\nEach key holds read or write permission per resource. Write includes read. Every endpoint lists the\r\npermission it needs. A call without it gets `403 Forbidden` with the missing permission in the error.\r\n\r\n| Resource | Covers |\r\n| --- | --- |\r\n| `tenants` | Tenants and sub-tenants |\r\n| `members` | Tenant members and invitations |\r\n| `agents` | Agents, agent templates, and the knowledge bases and phone numbers assigned to an agent |\r\n| `conversations` | Conversations (**read only**: conversations are created by the agent runtime) |\r\n| `usage` | Usage reports for billing (**read only**) |\r\n| `knowledge` | Knowledge bases and their documents |\r\n| `phone_numbers` | Phone numbers |\r\n\r\n## Tenants\r\nA key belongs to one tenant and can act on that tenant **and all its sub-tenants**, like an admin of that\r\ntenant. Start with `GET /v1/me` (the key and its tenant) and `GET /v1/tenants` (every tenant it can reach).\r\nA new customer is created under its parent: `POST /v1/tenants/current/sub-tenants`.\r\n\r\nEverything a tenant owns lives under `/v1/tenants/{tenantId}/...`: members, agents, conversations, usage,\r\nknowledge bases and phone numbers. `current` stands for the key's own tenant, so\r\n`GET /v1/tenants/current/agents` lists your own agents and `GET /v1/tenants/{subTenantId}/agents` a\r\ncustomer's. A tenant outside the key's reach gives `403`; an object that does not belong to the tenant in\r\nthe URL gives `404`. New objects are created in the tenant in the URL: request bodies carry no `tenantId`\r\n(responses still do).\r\n\r\n## Requests and responses\r\n- JSON in and out (`Content-Type: application/json`), except file uploads (`multipart/form-data`) and\r\n  recording downloads.\r\n- Property names are camelCase. Timestamps are ISO 8601 in UTC.\r\n- IDs are opaque strings; some resources use UUIDs. Treat both as opaque.\r\n- Updates are full replacements of the documented fields unless an endpoint says otherwise.\r\n- Optimistic concurrency: send back the `version`, `revision` or `rowVersion` you read. If someone else changed\r\n  the resource in between, you get `409 Conflict`; reload and retry.\r\n\r\n## Errors\r\nErrors return a JSON body with an `error` message (validation errors may use the standard\r\n`application/problem+json` shape with an `errors` object).\r\n\r\n| Status | Meaning |\r\n| --- | --- |\r\n| `400` | Invalid input; the message says what to fix |\r\n| `401` | Missing, invalid, revoked or expired API key |\r\n| `403` | Key lacks the permission, or the tenant / resource is outside its reach |\r\n| `404` | Not found (or not visible to this key) |\r\n| `409` | Conflict: name already taken, stale `version`/`revision`, or the resource is still in use |\r\n| `429` | Rate limit exceeded; retry after the number of seconds in the `Retry-After` header |\r\n\r\n## Rate limits\r\nRequests are limited per API key. When exceeded you get `429` with a `Retry-After` header.\r\n\r\n## Versioning\r\nThis is version 1 (`/v1`). Changes within v1 are backward compatible: we add endpoints, optional fields and\r\nnew enum values, and never change the meaning of existing ones. Ignore fields you don't recognise. If we ever\r\nneed to retire or change something, it is announced in the changelog well ahead of time, and anything\r\nbreaking comes as a new version, with v1 kept running for a transition period.",
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.envocal.ai",
      "description": "Envocal API (EU)"
    }
  ],
  "paths": {
    "/v1/tenants/{tenantId}/agents": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "List agents of a tenant",
        "description": "**Required permission:** `agents:read`",
        "operationId": "Agents_List",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tenant's agents (templates included; see `isTemplate`).",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AgentDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AgentDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AgentDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Create an agent",
        "description": "Creates the agent in the tenant together with a default widget configuration whose settings are all\nunset (the widget's defaults apply; change them with `PUT /v1/tenants/{tenantId}/agents/{agentId}/widget-configs/{id}`). Omitted voice settings get\ndefaults (`server_vad` turn detection, `gpt-4o-transcribe` transcription, `far_field` noise reduction).\nAssign tools with `PUT /v1/tenants/{tenantId}/agents/{id}/tools` and a phone number with\n`POST /v1/tenants/{tenantId}/agents/{agentId}/phone-numbers/{phoneNumberId}`.\n\n**Required permission:** `agents:write`",
        "operationId": "Agents_Create",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The new agent.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AgentCreateDto"
                  }
                ],
                "description": "A new agent. Omitted optional fields get defaults."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AgentCreateDto"
                  }
                ],
                "description": "A new agent. Omitted optional fields get defaults."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AgentCreateDto"
                  }
                ],
                "description": "A new agent. Omitted optional fields get defaults."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. The `Location` header points to the new agent.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid follow-up email, or knowledge bases not in the agent's tenant (listed in `missing`)."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/agents/{id}": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "Get an agent",
        "description": "The agent's settings. Use `GET /v1/agents/{id}/full` to also get its knowledge bases and tools expanded.\n\n**Required permission:** `agents:read`",
        "operationId": "Agents_Get",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The agent.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "put": {
        "tags": [
          "Agents"
        ],
        "summary": "Update an agent",
        "description": "Replaces the agent's settings. Send the `version` you read; it changes on every update.\nVoice settings left null keep their current value. `knowledgeBaseIds`: null keeps the current links,\n`[]` removes all, a list replaces them. The tenant cannot be changed.\n\n**Required permission:** `agents:write`",
        "operationId": "Agents_Update",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "New values.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AgentUpdateDto"
                  }
                ],
                "description": "New values for an agent. Fields are replaced; voice settings left null keep their current value."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AgentUpdateDto"
                  }
                ],
                "description": "New values for an agent. Fields are replaced; voice settings left null keep their current value."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AgentUpdateDto"
                  }
                ],
                "description": "New values for an agent. Fields are replaced; voice settings left null keep their current value."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Updated."
          },
          "400": {
            "description": "Invalid follow-up email, or knowledge bases not in the agent's tenant."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found."
          },
          "409": {
            "description": "`version` is stale: the agent was changed in the meantime. Reload and retry."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "delete": {
        "tags": [
          "Agents"
        ],
        "summary": "Delete an agent",
        "description": "Deletes the agent and its widget configurations and tool assignments. Its phone number becomes unassigned. Past conversations are kept.\n\n**Required permission:** `agents:write`",
        "operationId": "Agents_Delete",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/agents/{agentId}/phone-numbers/{phoneNumberId}": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Assign a phone number to an agent",
        "description": "Calls to the number are then answered by this agent. An agent has at most one number; assigning another replaces it. The number must belong to the agent's tenant and not be assigned to another agent.\n\n**Required permission:** `agents:write`",
        "operationId": "Agents_AssignPhoneNumber",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "phoneNumberId",
            "in": "path",
            "description": "Phone number id (from the phone number list).",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Assigned (also when it already was)."
          },
          "400": {
            "description": "The number belongs to a different tenant than the agent."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Agent or phone number not found."
          },
          "409": {
            "description": "The number is assigned to another agent."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/agents/{agentId}/phone-numbers": {
      "delete": {
        "tags": [
          "Agents"
        ],
        "summary": "Unassign an agent's phone number",
        "description": "**Required permission:** `agents:write`",
        "operationId": "Agents_UnassignPhoneNumber",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Unassigned (also when it had none)."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Agent not found."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/agents/{id}/knowledge-bases": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "List an agent's knowledge bases",
        "description": "**Required permission:** `agents:read`",
        "operationId": "Agents_GetKbs",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Linked knowledge bases (empty list if none).",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeBaseDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeBaseDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeBaseDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Agent not found."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/agents/{agentId}/knowledge-bases/{kbId}": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Link a knowledge base to an agent",
        "description": "The agent can then search the knowledge base during conversations. The knowledge base must belong to the agent's tenant.\n\n**Required permission:** `agents:write`",
        "operationId": "Agents_LinkKB",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kbId",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Linked (also when it already was)."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Agent not found, or no such knowledge base in the agent's tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "delete": {
        "tags": [
          "Agents"
        ],
        "summary": "Unlink a knowledge base from an agent",
        "description": "The knowledge base itself is not deleted.\n\n**Required permission:** `agents:write`",
        "operationId": "Agents_UnlinkKB",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "path",
            "description": "Agent id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kbId",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Unlinked."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Agent not found."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/agent-templates": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "List agent templates",
        "description": "Templates (agents with `isTemplate` true) that the tenant can use: its own and those of its parent tenants.\n\n**Required permission:** `agents:read`",
        "operationId": "Agents_GetVisibleTemplates",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The templates, sorted by name.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AgentTemplateListItemDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AgentTemplateListItemDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AgentTemplateListItemDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/agent-templates/{templateId}/instantiate": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Create an agent from a template",
        "description": "Copies the template's instructions, variables and voice settings into a new `draft` agent in the tenant, with a\ndefault widget configuration whose settings are all unset (the widget's defaults apply). Phone number,\nknowledge bases and tools are not copied.\n\n**Required permission:** `agents:write`",
        "operationId": "Agents_InstantiateTemplate",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "templateId",
            "in": "path",
            "description": "Template id (from the template list).",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Name of the new agent.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/InstantiateTemplateDto"
                  }
                ],
                "description": "Options for creating an agent from a template."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/InstantiateTemplateDto"
                  }
                ],
                "description": "Options for creating an agent from a template."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/InstantiateTemplateDto"
                  }
                ],
                "description": "Options for creating an agent from a template."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. The `Location` header points to the new agent.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentDto"
                }
              }
            }
          },
          "400": {
            "description": "Missing name."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "No such template visible to the tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/conversations/{id}": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get a conversation",
        "description": "Who talked to which agent and when, the transcript, the summary and how it ended.\n\n**Required permission:** `conversations:read`",
        "operationId": "Conversations_GetPublic",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Conversation id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConversationDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConversationDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConversationDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/conversations": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List conversations of a tenant",
        "description": "Newest first, paged. Combine the filters as needed. Get one conversation for its transcript and summary.\n\n**Required permission:** `conversations:read`",
        "operationId": "Conversations_ListPublic",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentName",
            "in": "query",
            "description": "Only conversations whose agent name contains this text.",
            "style": "form",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "channels",
            "in": "query",
            "description": "Only these channels; repeat the parameter for several (`?channels=phone&channels=text_chat`). Values: `phone`, `voice_chat`, `text_chat` (older data may contain `text_voice_chat`).",
            "style": "form",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "startFrom",
            "in": "query",
            "description": "Only conversations that started at or after this time (ISO 8601).",
            "style": "form",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "startTo",
            "in": "query",
            "description": "Only conversations that started at or before this time (ISO 8601).",
            "style": "form",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1.",
            "style": "form",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Items per page, 1–200. Default 10.",
            "style": "form",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of conversations.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConversationListItemDtoPagedResult"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConversationListItemDtoPagedResult"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConversationListItemDtoPagedResult"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/knowledge-bases": {
      "get": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "List knowledge bases of a tenant",
        "description": "**Required permission:** `knowledge:read`",
        "operationId": "KnowledgeBases_List",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The knowledge bases.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeBaseDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeBaseDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeBaseDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "post": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "Create a knowledge base",
        "description": "An empty knowledge base; add documents with `POST /v1/tenants/{tenantId}/knowledge-bases/{kbId}/documents` and link it to agents.\n\n**Required permission:** `knowledge:write`",
        "operationId": "KnowledgeBases_CreateKB",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The knowledge base.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/KnowledgeBaseCreateDto"
                  }
                ],
                "description": "A new knowledge base."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/KnowledgeBaseCreateDto"
                  }
                ],
                "description": "A new knowledge base."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/KnowledgeBaseCreateDto"
                  }
                ],
                "description": "A new knowledge base."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeBaseDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeBaseDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeBaseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "409": {
            "description": "A knowledge base with this name already exists in the tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/knowledge-bases/{id}": {
      "put": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "Rename or describe a knowledge base",
        "description": "**Required permission:** `knowledge:write`",
        "operationId": "KnowledgeBases_UpdateKB",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "New name and description.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/KnowledgeBaseUpdateDto"
                  }
                ],
                "description": "New name and description for a knowledge base."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/KnowledgeBaseUpdateDto"
                  }
                ],
                "description": "New name and description for a knowledge base."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/KnowledgeBaseUpdateDto"
                  }
                ],
                "description": "New name and description for a knowledge base."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Updated."
          },
          "400": {
            "description": "`id` in the body differs from the route."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "409": {
            "description": "The name is already used in the tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "delete": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "Delete a knowledge base",
        "description": "Deletes the knowledge base and all its documents. Agents linked to it lose access.\n\n**Required permission:** `knowledge:write`",
        "operationId": "KnowledgeBases_DeleteKB",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          },
          "503": {
            "description": "The knowledge service is unavailable; nothing was deleted. Retry later."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/knowledge-bases/{kbId}/documents": {
      "get": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "List documents in a knowledge base",
        "description": "**Required permission:** `knowledge:read`",
        "operationId": "KnowledgeBases_ListDocs",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kbId",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The documents (empty if the knowledge base is not found in this tenant).",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeDocumentDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeDocumentDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeDocumentDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "post": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "Upload a document",
        "description": "Send `multipart/form-data` with the document in a `file` field (e.g. PDF, Word, plain text or Markdown),\none document per request, at most 20 MB.\nThe document is processed in the background: poll `POST /v1/tenants/{tenantId}/knowledge-bases/{kbId}/documents/status`\nwith the returned `id` until its state is `complete`.\n\n**Required permission:** `knowledge:write`",
        "operationId": "KnowledgeBases_UploadDoc",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kbId",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Accepted for processing. Body: `{ \"id\": \"<document id>\" }`."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Knowledge base not found in this tenant."
          },
          "413": {
            "description": "The document is larger than 20 MB."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/knowledge-bases/{kbId}/documents/{docId}": {
      "delete": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "Delete a document",
        "description": "**Required permission:** `knowledge:write`",
        "operationId": "KnowledgeBases_DeleteDoc",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kbId",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "docId",
            "in": "path",
            "description": "Document id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          },
          "503": {
            "description": "The knowledge service is unavailable; the document was not deleted. Retry later."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/knowledge-bases/{kbId}/documents/status": {
      "post": {
        "tags": [
          "Knowledge bases"
        ],
        "summary": "Get processing status of documents",
        "description": "Poll after uploading until each document's state is `complete` (or `error`).\n\n**Required permission:** `knowledge:read`",
        "operationId": "KnowledgeBases_GetStatuses",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kbId",
            "in": "path",
            "description": "Knowledge base id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Document ids to check.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/StatusRequest"
                  }
                ],
                "description": "Documents to check."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/StatusRequest"
                  }
                ],
                "description": "Documents to check."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/StatusRequest"
                  }
                ],
                "description": "Documents to check."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status per document id.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/UploadStatusDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/UploadStatusDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/UploadStatusDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Knowledge base not found in this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "Me"
        ],
        "summary": "Identify the caller",
        "description": "The API key you are calling with and its tenant. Start here: `tenantId` is what `current` stands for in\n`/v1/tenants/current/...`, and `GET /v1/tenants` lists that tenant with all its sub-tenants. Works with any\nvalid key, whatever its permissions.\n\n**Required permission:** none; any valid API key.",
        "operationId": "Me_Get",
        "responses": {
          "200": {
            "description": "The key and its tenant.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MeDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/members": {
      "get": {
        "tags": [
          "Members & invites"
        ],
        "summary": "List members of a tenant",
        "description": "Direct members only, sorted by email. Members of parent tenants also have access but are listed at their own tenant.\n\n**Required permission:** `members:read`",
        "operationId": "Tenants_GetMembers",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The members.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TenantMemberDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TenantMemberDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TenantMemberDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/members/{userId}": {
      "patch": {
        "tags": [
          "Members & invites"
        ],
        "summary": "Change a member's role",
        "description": "**Required permission:** `members:write`",
        "operationId": "Tenants_ChangeMemberRole",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "userId",
            "in": "path",
            "description": "User id of the member (from the member list).",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The new role.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangeRoleDto"
                  }
                ],
                "description": "A new role for a member."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangeRoleDto"
                  }
                ],
                "description": "A new role for a member."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangeRoleDto"
                  }
                ],
                "description": "A new role for a member."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Role changed."
          },
          "400": {
            "description": "`Owner` is not assignable."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "The user is not a direct member of this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "delete": {
        "tags": [
          "Members & invites"
        ],
        "summary": "Remove a member",
        "description": "Removes the user's membership of this tenant. The user account itself is not deleted.\n\n**Required permission:** `members:write`",
        "operationId": "Tenants_RemoveMember",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "userId",
            "in": "path",
            "description": "User id of the member.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "The user is not a direct member of this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/invites": {
      "get": {
        "tags": [
          "Members & invites"
        ],
        "summary": "List pending invitations",
        "description": "Invitations that have not been accepted yet, sorted by email. Expired ones are included; check `expiresUtc`.\n\n**Required permission:** `members:read`",
        "operationId": "Tenants_ListInvites",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The pending invitations.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InviteListItemDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InviteListItemDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InviteListItemDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "post": {
        "tags": [
          "Members & invites"
        ],
        "summary": "Invite a user",
        "description": "- If an account with this email exists, the user is added to the tenant directly and notified by email (`204`).\n- Otherwise an invitation email is sent with a link that is valid for 7 days (`202`). Inviting the same email\nagain updates the role and resends the invitation.\n\n**Required permission:** `members:write`",
        "operationId": "Tenants_CreateInvite",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Who to invite and with which role.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/InviteCreateDto"
                  }
                ],
                "description": "An invitation to join a tenant."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/InviteCreateDto"
                  }
                ],
                "description": "An invitation to join a tenant."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/InviteCreateDto"
                  }
                ],
                "description": "An invitation to join a tenant."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Invitation sent."
          },
          "204": {
            "description": "Existing user added directly."
          },
          "400": {
            "description": "`Owner` is not assignable."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "409": {
            "description": "The user is already a member here, or of a parent tenant (and inherits access)."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/invites/{inviteId}/resend": {
      "post": {
        "tags": [
          "Members & invites"
        ],
        "summary": "Resend an invitation",
        "description": "Sends the invitation email again and extends its validity to 7 days from now.\n\n**Required permission:** `members:write`",
        "operationId": "Tenants_ResendInvite",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "inviteId",
            "in": "path",
            "description": "Invitation id (from the invitation list).",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Invitation resent."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "No pending invitation with this id."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/invites/{inviteId}": {
      "delete": {
        "tags": [
          "Members & invites"
        ],
        "summary": "Withdraw an invitation",
        "description": "Deletes a pending invitation; its link stops working.\n\n**Required permission:** `members:write`",
        "operationId": "Tenants_WithdrawInvite",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "inviteId",
            "in": "path",
            "description": "Invitation id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "No pending invitation with this id."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/phone-numbers": {
      "get": {
        "tags": [
          "Phone numbers"
        ],
        "summary": "List phone numbers of a tenant",
        "description": "**Required permission:** `phone_numbers:read`",
        "operationId": "PhoneNumbers_List",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The phone numbers with the agent each is assigned to.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PhoneNumberDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PhoneNumberDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PhoneNumberDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "post": {
        "tags": [
          "Phone numbers"
        ],
        "summary": "Add a phone number",
        "description": "Registers a number to the tenant so it can be assigned to an agent. Calls to the number must also be routed to\nthe platform by your telephony provider (SIP trunk). Numbers are unique across the platform.\n\n**Required permission:** `phone_numbers:write`",
        "operationId": "PhoneNumbers_Create",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The number.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/PhoneNumberCreateDto"
                  }
                ],
                "description": "A new phone number."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/PhoneNumberCreateDto"
                  }
                ],
                "description": "A new phone number."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/PhoneNumberCreateDto"
                  }
                ],
                "description": "A new phone number."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberDto"
                }
              }
            }
          },
          "400": {
            "description": "Not in E.164 format."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "409": {
            "description": "The number already exists."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/phone-numbers/{id}": {
      "get": {
        "tags": [
          "Phone numbers"
        ],
        "summary": "Get a phone number",
        "description": "**Required permission:** `phone_numbers:read`",
        "operationId": "PhoneNumbers_Get",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Phone number id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The phone number.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "put": {
        "tags": [
          "Phone numbers"
        ],
        "summary": "Change a phone number",
        "description": "Changes the number itself; its tenant and agent assignment stay the same.\n\n**Required permission:** `phone_numbers:write`",
        "operationId": "PhoneNumbers_Update",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Phone number id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The new number.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/PhoneNumberUpdateDto"
                  }
                ],
                "description": "A changed phone number."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/PhoneNumberUpdateDto"
                  }
                ],
                "description": "A changed phone number."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/PhoneNumberUpdateDto"
                  }
                ],
                "description": "A changed phone number."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Updated."
          },
          "400": {
            "description": "Not in E.164 format."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "409": {
            "description": "The number already exists."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "delete": {
        "tags": [
          "Phone numbers"
        ],
        "summary": "Delete a phone number",
        "description": "**Required permission:** `phone_numbers:write`",
        "operationId": "PhoneNumbers_Delete",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "description": "Phone number id.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Not found in this tenant."
          },
          "409": {
            "description": "The number is assigned to an agent; unassign it first."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants": {
      "get": {
        "tags": [
          "Tenants"
        ],
        "summary": "List tenants",
        "description": "Every tenant the caller can act on: the key's own tenant and all its sub-tenants, sorted by name.\n\n**Required permission:** `tenants:read`",
        "operationId": "Tenants_List",
        "parameters": [
          {
            "name": "extraId",
            "in": "query",
            "description": "Only tenants with this external id (the `extraId` you gave them, e.g. your own customer number).",
            "style": "form",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The reachable tenants.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TenantDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TenantDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TenantDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}": {
      "get": {
        "tags": [
          "Tenants"
        ],
        "summary": "Get a tenant",
        "description": "**Required permission:** `tenants:read`",
        "operationId": "Tenants_GetById",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tenant.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Tenant not found."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      },
      "put": {
        "tags": [
          "Tenants"
        ],
        "summary": "Update a tenant",
        "description": "Replaces the tenant's name, domain and external id. The tenant type cannot be changed.\n\n**Required permission:** `tenants:write`",
        "operationId": "Tenants_Update",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "New values.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TenantUpdateDto"
                  }
                ],
                "description": "New values for a tenant. The type cannot be changed."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TenantUpdateDto"
                  }
                ],
                "description": "New values for a tenant. The type cannot be changed."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TenantUpdateDto"
                  }
                ],
                "description": "New values for a tenant. The type cannot be changed."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Updated."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Tenant not found."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/sub-tenants": {
      "post": {
        "tags": [
          "Tenants"
        ],
        "summary": "Create a sub-tenant",
        "description": "Creates a tenant under the tenant in the URL (`current` for the key's own tenant, or the id of one of its\nsub-tenants).\n\nAllowed parent → child types: `main` → `main`, `sub`, `customer`; `sub` → `customer`. A `customer` tenant cannot\nhave children.\n\n**Required permission:** `tenants:write`",
        "operationId": "Tenants_Create",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The new tenant.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TenantCreateDto"
                  }
                ],
                "description": "A new tenant, created under the tenant in the URL."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TenantCreateDto"
                  }
                ],
                "description": "A new tenant, created under the tenant in the URL."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TenantCreateDto"
                  }
                ],
                "description": "A new tenant, created under the tenant in the URL."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. The `Location` header points to the new tenant.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid type or region, or the parent type cannot contain this type."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    },
    "/v1/tenants/{tenantId}/usage": {
      "get": {
        "tags": [
          "Usage"
        ],
        "summary": "Get usage of a tenant",
        "description": "Usage in a period for billing: voice conversations (phone and web voice) in time, text chats in input and output\ntokens. Lines are per agent and knowledge-base state, and every total is split into with and without knowledge\nbase, so different per-minute prices can be applied.\n\nWith `includeSubtenants=true` the response nests all sub-tenants (to any depth) with their own lines and totals,\nplus `totalIncludingSubtenants` at every level; sub-tenant branches without usage are left out.\n\nConversations are counted in the period they started in. Use `durationSeconds` for exact billing or\n`durationMinutes` (decimal minutes) to multiply by a per-minute price.\n\n**Required permission:** `usage:read`",
        "operationId": "Tenants_GetUsage",
        "parameters": [
          {
            "name": "tenantId",
            "in": "path",
            "description": "Tenant id, or `current` for the key's own tenant. Must be the key's tenant or one of its sub-tenants.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Start of the period, inclusive, ISO 8601 UTC, e.g. `2026-09-01T00:00:00Z`.",
            "required": true,
            "style": "form",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "End of the period, exclusive, ISO 8601 UTC, e.g. `2026-10-01T00:00:00Z` for all of September. At most 366 days after `from`.",
            "required": true,
            "style": "form",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "includeSubtenants",
            "in": "query",
            "description": "Also report all sub-tenants. Default false: only this tenant.",
            "style": "form",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The usage report.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TenantUsageReportDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantUsageReportDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantUsageReportDto"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid `from`/`to`, `from` not before `to`, or a period longer than 366 days."
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key."
          },
          "403": {
            "description": "The key lacks the required permission, or the tenant/resource is outside its reach."
          },
          "404": {
            "description": "Tenant not found."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AgentCreateDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "type": "string",
            "description": "Internal description; not given to the AI.",
            "nullable": true
          },
          "instructions": {
            "type": "string",
            "description": "System prompt. May reference prompt variables as `{{name}}` and system variables such as `{{system_caller_number}}`.",
            "nullable": true
          },
          "variableDefinitionsJson": {
            "type": "string",
            "description": "Prompt variables as a JSON-encoded string: an array of `{\"name\", \"jsonType\", \"placeholder\", \"description\", \"required\"}`.",
            "nullable": true
          },
          "status": {
            "enum": [
              "draft",
              "active",
              "archived"
            ],
            "type": "string",
            "description": "Lifecycle status. Default `draft`.",
            "nullable": true
          },
          "model": {
            "type": "string",
            "description": "Realtime model, e.g. `gpt-realtime`. Omit for the platform default.",
            "nullable": true
          },
          "voice": {
            "enum": [
              "alloy",
              "ash",
              "ballad",
              "coral",
              "echo",
              "sage",
              "shimmer",
              "verse",
              "marin",
              "cedar"
            ],
            "type": "string",
            "description": "Voice for speech output.",
            "nullable": true
          },
          "knowledgeBaseIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Knowledge bases to link; they must belong to `tenantId`.",
            "nullable": true
          },
          "allowFollowUpEmail": {
            "type": "boolean",
            "description": "Give the AI a `send_email` tool for follow-up emails. Default false.",
            "nullable": true
          },
          "followUpEmailAddress": {
            "type": "string",
            "description": "Follow-up email recipient; required when `allowFollowUpEmail` is true.",
            "nullable": true
          },
          "allowCallTransfer": {
            "type": "boolean",
            "description": "Give the AI a `transfer_call` tool on phone calls. Default true.",
            "nullable": true
          },
          "callTransferType": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CallTransferType"
              }
            ],
            "description": "How calls are transferred. Default `direct`.",
            "nullable": true
          },
          "isTemplate": {
            "type": "boolean",
            "description": "Make this agent a template for this tenant and its sub-tenants. Default false.",
            "nullable": true
          },
          "type": {
            "type": "string",
            "description": "Optional free-text label; not used by the platform.",
            "nullable": true
          },
          "allowEndCall": {
            "type": "boolean",
            "description": "Give the AI an `end_call` tool. Default false.",
            "nullable": true
          },
          "turnDetectionType": {
            "enum": [
              "server_vad",
              "semantic_vad",
              "none"
            ],
            "type": "string",
            "description": "`server_vad` (default), `semantic_vad` or `none`.",
            "nullable": true
          },
          "turnDetectionEagerness": {
            "enum": [
              "auto",
              "low",
              "medium",
              "high"
            ],
            "type": "string",
            "description": "`semantic_vad` only. Default `auto`.",
            "nullable": true
          },
          "turnDetectionThreshold": {
            "type": "number",
            "description": "`server_vad` only, 0.0–1.0. Default 0.5.",
            "format": "double",
            "nullable": true
          },
          "turnDetectionPrefixPadding": {
            "type": "integer",
            "description": "`server_vad` only, milliseconds. Default 300.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionSilenceDuration": {
            "type": "integer",
            "description": "`server_vad` only, milliseconds. Default 500.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionTimeout": {
            "type": "boolean",
            "description": "`server_vad` only: speak up after a period of user silence. Default false.",
            "nullable": true
          },
          "turnDetectionTimeoutTime": {
            "type": "integer",
            "description": "Idle timeout in milliseconds; required when `turnDetectionTimeout` is true.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionInterruptResponse": {
            "type": "boolean",
            "description": "Let the user interrupt the AI. Default true.",
            "nullable": true
          },
          "transcriptionModel": {
            "type": "string",
            "description": "`gpt-4o-transcribe` (default), `gpt-4o-mini-transcribe` or `off`.",
            "nullable": true
          },
          "transcriptionLanguage": {
            "type": "string",
            "description": "ISO 639-1 code or `auto` (default).",
            "nullable": true
          },
          "transcriptionInstructions": {
            "type": "string",
            "description": "Optional hints for the transcriber.",
            "nullable": true
          },
          "noiseReductionType": {
            "enum": [
              "none",
              "near_field",
              "far_field"
            ],
            "type": "string",
            "description": "`near_field`, `far_field` (default) or `none`.",
            "nullable": true
          },
          "toolAssignments": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AgentToolsConfigDto"
              }
            ],
            "description": "Ignored; assign tools with `PUT /v1/tenants/{tenantId}/agents/{id}/tools` after creating the agent.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A new agent. Omitted optional fields get defaults."
      },
      "AgentDto": {
        "required": [
          "id",
          "name",
          "tenantId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Agent id."
          },
          "version": {
            "type": "integer",
            "description": "Concurrency version. Send it back when updating; it increases on every update.",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "type": "string",
            "description": "Internal description for people; not given to the AI.",
            "nullable": true
          },
          "instructions": {
            "type": "string",
            "description": "System prompt for the AI. May reference prompt variables as `{{name}}` (see `variableDefinitionsJson`) and system variables such as `{{system_caller_number}}`.",
            "nullable": true
          },
          "variableDefinitionsJson": {
            "type": "string",
            "description": "Prompt variables as a JSON-encoded string: an array of `{\"name\", \"jsonType\", \"placeholder\", \"description\", \"required\"}`. Referenced as `{{name}}` in the instructions and filled at conversation time (e.g. from tool output); `placeholder` is used when no value is available.",
            "nullable": true
          },
          "status": {
            "enum": [
              "draft",
              "active",
              "archived"
            ],
            "type": "string",
            "description": "Lifecycle status.",
            "nullable": true
          },
          "model": {
            "type": "string",
            "description": "Realtime model, e.g. `gpt-realtime` or `gpt-realtime-1.5`. Empty means the platform default.",
            "nullable": true
          },
          "voice": {
            "enum": [
              "alloy",
              "ash",
              "ballad",
              "coral",
              "echo",
              "sage",
              "shimmer",
              "verse",
              "marin",
              "cedar"
            ],
            "type": "string",
            "description": "Voice used for speech output.",
            "nullable": true
          },
          "knowledgeBaseIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Ids of linked knowledge bases.",
            "nullable": true
          },
          "phoneNumberId": {
            "type": "string",
            "description": "Id of the assigned phone number, if any. Change it with the agent phone-number endpoints.",
            "nullable": true
          },
          "phoneNumber": {
            "type": "string",
            "description": "The assigned phone number in E.164 format (read only).",
            "nullable": true
          },
          "allowFollowUpEmail": {
            "type": "boolean",
            "description": "Give the AI a `send_email` tool that emails conversation details to `followUpEmailAddress`."
          },
          "followUpEmailAddress": {
            "type": "string",
            "description": "Recipient of follow-up emails (e.g. a team inbox). Required when `allowFollowUpEmail` is true.",
            "nullable": true
          },
          "allowCallTransfer": {
            "type": "boolean",
            "description": "Give the AI a `transfer_call` tool on phone calls."
          },
          "callTransferType": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CallTransferType"
              }
            ],
            "description": "How phone calls are transferred."
          },
          "isTemplate": {
            "type": "boolean",
            "description": "When true the agent is a template that this tenant and its sub-tenants can create agents from."
          },
          "type": {
            "type": "string",
            "description": "Optional free-text label for your own classification; not used by the platform.",
            "nullable": true
          },
          "allowEndCall": {
            "type": "boolean",
            "description": "Give the AI an `end_call` tool to end the conversation after saying goodbye."
          },
          "tenantId": {
            "type": "string",
            "description": "Tenant the agent belongs to (cannot be changed)."
          },
          "turnDetectionType": {
            "enum": [
              "server_vad",
              "semantic_vad",
              "none"
            ],
            "type": "string",
            "description": "How the AI decides the user finished speaking: `server_vad` (silence based), `semantic_vad` (meaning based) or `none` (no automatic turn taking).",
            "nullable": true
          },
          "turnDetectionEagerness": {
            "enum": [
              "auto",
              "low",
              "medium",
              "high"
            ],
            "type": "string",
            "description": "`semantic_vad` only: how quickly the AI responds.",
            "nullable": true
          },
          "turnDetectionThreshold": {
            "type": "number",
            "description": "`server_vad` only: speech detection sensitivity, 0.0–1.0 (higher needs louder speech). Default 0.5.",
            "format": "double",
            "nullable": true
          },
          "turnDetectionPrefixPadding": {
            "type": "integer",
            "description": "`server_vad` only: milliseconds of audio kept before detected speech. Default 300.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionSilenceDuration": {
            "type": "integer",
            "description": "`server_vad` only: milliseconds of silence that end the user's turn. Default 500.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionTimeout": {
            "type": "boolean",
            "description": "`server_vad` only: when true, the AI speaks up after `turnDetectionTimeoutTime` of user silence.",
            "nullable": true
          },
          "turnDetectionTimeoutTime": {
            "type": "integer",
            "description": "Idle timeout in milliseconds; required when `turnDetectionTimeout` is true.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionInterruptResponse": {
            "type": "boolean",
            "description": "Whether the user can interrupt the AI while it is speaking. Default true.",
            "nullable": true
          },
          "transcriptionModel": {
            "type": "string",
            "description": "Speech-to-text model for transcripts: `gpt-4o-transcribe` (precision) or `gpt-4o-mini-transcribe` (speed). `off` disables transcription.",
            "nullable": true
          },
          "transcriptionLanguage": {
            "type": "string",
            "description": "Expected language as an ISO 639-1 code (e.g. `sv`, `en`), or `auto` to detect.",
            "nullable": true
          },
          "transcriptionInstructions": {
            "type": "string",
            "description": "Optional hints for the transcriber, e.g. product names or spelling.",
            "nullable": true
          },
          "noiseReductionType": {
            "enum": [
              "none",
              "near_field",
              "far_field"
            ],
            "type": "string",
            "description": "Input noise reduction: `near_field` (headsets, phones), `far_field` (laptops, rooms) or `none`.",
            "nullable": true
          },
          "toolAssignments": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AgentToolsConfigDto"
              }
            ],
            "description": "Not filled here; use `GET /v1/tenants/{tenantId}/agents/{id}/tools`.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "An AI voice/chat agent."
      },
      "AgentTemplateListItemDto": {
        "required": [
          "id",
          "name",
          "tenantId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Template (agent) id."
          },
          "name": {
            "type": "string",
            "description": "Template name."
          },
          "description": {
            "type": "string",
            "description": "Template description.",
            "nullable": true
          },
          "tenantId": {
            "type": "string",
            "description": "Tenant that owns the template (this tenant or a parent)."
          },
          "tenantName": {
            "type": "string",
            "description": "Name of that tenant.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "An agent template that can be instantiated."
      },
      "AgentToolsConfigDto": {
        "type": "object",
        "properties": {
          "before": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Run in this order when the conversation starts, before the AI speaks (e.g. look up the caller in a CRM)."
          },
          "during": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Offered to the AI model as functions it may call during the conversation. Order does not matter."
          },
          "after": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Run in this order after the conversation ends (e.g. post a summary or create a ticket)."
          }
        },
        "additionalProperties": false,
        "description": "Tool instances assigned to an agent, per phase. Ids come from `GET /v1/tenants/{tenantId}/tenant-tools`."
      },
      "AgentUpdateDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "version": {
            "type": "integer",
            "description": "The `version` you read. A stale version gives 409.",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "type": "string",
            "description": "Internal description; not given to the AI.",
            "nullable": true
          },
          "instructions": {
            "type": "string",
            "description": "System prompt.",
            "nullable": true
          },
          "variableDefinitionsJson": {
            "type": "string",
            "description": "Prompt variables as a JSON-encoded string (see Agent.variableDefinitionsJson).",
            "nullable": true
          },
          "status": {
            "enum": [
              "draft",
              "active",
              "archived"
            ],
            "type": "string",
            "description": "Lifecycle status; null keeps the current value.",
            "nullable": true
          },
          "model": {
            "type": "string",
            "description": "Realtime model; null keeps the current value.",
            "nullable": true
          },
          "voice": {
            "enum": [
              "alloy",
              "ash",
              "ballad",
              "coral",
              "echo",
              "sage",
              "shimmer",
              "verse",
              "marin",
              "cedar"
            ],
            "type": "string",
            "description": "Voice; null keeps the current value.",
            "nullable": true
          },
          "knowledgeBaseIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "null keeps the current links, `[]` removes all, a list replaces them.",
            "nullable": true
          },
          "allowFollowUpEmail": {
            "type": "boolean",
            "description": "Give the AI a `send_email` tool for follow-up emails."
          },
          "followUpEmailAddress": {
            "type": "string",
            "description": "Follow-up email recipient; required when `allowFollowUpEmail` is true.",
            "nullable": true
          },
          "allowCallTransfer": {
            "type": "boolean",
            "description": "Give the AI a `transfer_call` tool on phone calls."
          },
          "callTransferType": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CallTransferType"
              }
            ],
            "description": "How calls are transferred; null keeps the current value.",
            "nullable": true
          },
          "isTemplate": {
            "type": "boolean",
            "description": "Whether the agent is a template."
          },
          "type": {
            "type": "string",
            "description": "Optional free-text label.",
            "nullable": true
          },
          "allowEndCall": {
            "type": "boolean",
            "description": "Give the AI an `end_call` tool."
          },
          "turnDetectionType": {
            "enum": [
              "server_vad",
              "semantic_vad",
              "none"
            ],
            "type": "string",
            "description": "`server_vad`, `semantic_vad` or `none`.",
            "nullable": true
          },
          "turnDetectionEagerness": {
            "enum": [
              "auto",
              "low",
              "medium",
              "high"
            ],
            "type": "string",
            "description": "`semantic_vad` only.",
            "nullable": true
          },
          "turnDetectionThreshold": {
            "type": "number",
            "description": "`server_vad` only, 0.0–1.0.",
            "format": "double",
            "nullable": true
          },
          "turnDetectionPrefixPadding": {
            "type": "integer",
            "description": "`server_vad` only, milliseconds.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionSilenceDuration": {
            "type": "integer",
            "description": "`server_vad` only, milliseconds.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionTimeout": {
            "type": "boolean",
            "description": "`server_vad` only: speak up after user silence.",
            "nullable": true
          },
          "turnDetectionTimeoutTime": {
            "type": "integer",
            "description": "Idle timeout in milliseconds.",
            "format": "int32",
            "nullable": true
          },
          "turnDetectionInterruptResponse": {
            "type": "boolean",
            "description": "Let the user interrupt the AI.",
            "nullable": true
          },
          "transcriptionModel": {
            "type": "string",
            "description": "`gpt-4o-transcribe`, `gpt-4o-mini-transcribe` or `off`.",
            "nullable": true
          },
          "transcriptionLanguage": {
            "type": "string",
            "description": "ISO 639-1 code or `auto`.",
            "nullable": true
          },
          "transcriptionInstructions": {
            "type": "string",
            "description": "Hints for the transcriber. Replaced as given (null clears).",
            "nullable": true
          },
          "noiseReductionType": {
            "enum": [
              "none",
              "near_field",
              "far_field"
            ],
            "type": "string",
            "description": "`near_field`, `far_field` or `none`.",
            "nullable": true
          },
          "toolAssignments": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AgentToolsConfigDto"
              }
            ],
            "description": "Ignored; use `PUT /v1/tenants/{tenantId}/agents/{id}/tools`.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "New values for an agent. Fields are replaced; voice settings left null keep their current value."
      },
      "AgentUsageDto": {
        "required": [
          "agentName",
          "text",
          "voice"
        ],
        "type": "object",
        "properties": {
          "agentId": {
            "type": "string",
            "description": "Agent id; null if the agent has been deleted since.",
            "nullable": true
          },
          "agentName": {
            "type": "string",
            "description": "Agent name (the name saved with the conversations if the agent has been deleted)."
          },
          "hasKnowledgeBase": {
            "type": "boolean",
            "description": "Whether the agent had a knowledge base during these conversations."
          },
          "voice": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VoiceUsageDto"
              }
            ],
            "description": "Voice usage (phone and web voice)."
          },
          "text": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TextUsageDto"
              }
            ],
            "description": "Text chat usage."
          }
        },
        "additionalProperties": false,
        "description": "Usage of one agent with or without a knowledge base. Typically one invoice line."
      },
      "CallTransferType": {
        "enum": [
          "direct",
          "semi_attended"
        ],
        "type": "string",
        "description": "How the AI transfers a phone call: `direct` is a blind transfer (the call is handed over right away); `semi_attended` first calls the target, checks that it is reachable and connects the caller once the target answers."
      },
      "ChangeRoleDto": {
        "type": "object",
        "properties": {
          "role": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantRole"
              }
            ],
            "description": "`User` or `Admin` (`Owner` is not assignable)."
          }
        },
        "additionalProperties": false,
        "description": "A new role for a member."
      },
      "InstantiateTemplateDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the new agent."
          }
        },
        "additionalProperties": false,
        "description": "Options for creating an agent from a template."
      },
      "InviteCreateDto": {
        "required": [
          "email"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "Email address to invite."
          },
          "role": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantRole"
              }
            ],
            "description": "Role to give: `User` or `Admin` (`Owner` is not assignable)."
          }
        },
        "additionalProperties": false,
        "description": "An invitation to join a tenant."
      },
      "InviteListItemDto": {
        "required": [
          "email",
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Invitation id."
          },
          "email": {
            "type": "string",
            "description": "Invited email address."
          },
          "role": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantRole"
              }
            ],
            "description": "Role the user gets on accepting."
          },
          "expiresUtc": {
            "type": "string",
            "description": "When the invitation link expires (UTC).",
            "format": "date-time"
          },
          "accepted": {
            "type": "boolean",
            "description": "Whether it has been accepted (always false in the pending list)."
          }
        },
        "additionalProperties": false,
        "description": "A pending invitation."
      },
      "KnowledgeBaseCreateDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name, unique per tenant."
          },
          "description": {
            "type": "string",
            "description": "Optional description.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A new knowledge base."
      },
      "KnowledgeBaseDto": {
        "required": [
          "id",
          "name",
          "tenantId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Knowledge base id."
          },
          "name": {
            "type": "string",
            "description": "Name, unique per tenant."
          },
          "description": {
            "type": "string",
            "description": "Optional description.",
            "nullable": true
          },
          "documentCount": {
            "type": "integer",
            "description": "Number of documents.",
            "format": "int32"
          },
          "tenantId": {
            "type": "string",
            "description": "Tenant it belongs to."
          }
        },
        "additionalProperties": false,
        "description": "A knowledge base: a set of documents agents can search during conversations."
      },
      "KnowledgeBaseUpdateDto": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Knowledge base id; must match the route."
          },
          "name": {
            "type": "string",
            "description": "Name, unique per tenant."
          },
          "description": {
            "type": "string",
            "description": "Description; null clears it.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "New name and description for a knowledge base."
      },
      "KnowledgeDocumentDto": {
        "required": [
          "fileName",
          "id",
          "mimeType"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Document id."
          },
          "fileName": {
            "type": "string",
            "description": "Original file name."
          },
          "mimeType": {
            "type": "string",
            "description": "File type, e.g. `application/pdf`."
          },
          "fileSize": {
            "type": "integer",
            "description": "Size in bytes.",
            "format": "int64"
          },
          "uploadedUtc": {
            "type": "string",
            "description": "Upload time (UTC).",
            "format": "date-time"
          },
          "version": {
            "type": "integer",
            "description": "Document version.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A document in a knowledge base."
      },
      "MeDto": {
        "required": [
          "name",
          "region",
          "scopes",
          "tenantId",
          "tenantName"
        ],
        "type": "object",
        "properties": {
          "keyId": {
            "type": "string",
            "description": "Id of the API key.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The name the key was given in the Studio."
          },
          "tenantId": {
            "type": "string",
            "description": "The key's own tenant: what `current` stands for in `/v1/tenants/current/...`."
          },
          "tenantName": {
            "type": "string",
            "description": "Name of that tenant."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The key's permissions, e.g. `agents:write`."
          },
          "region": {
            "type": "string",
            "description": "Data region the key and its tenant live in, e.g. `eu`; the same code is in the key (`evk_eu_...`)."
          }
        },
        "additionalProperties": false,
        "description": "The caller of the public API: an API key and its tenant."
      },
      "PhoneNumberCreateDto": {
        "required": [
          "number"
        ],
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "The number in E.164 format, e.g. `+46812345678`."
          }
        },
        "additionalProperties": false,
        "description": "A new phone number."
      },
      "PhoneNumberDto": {
        "required": [
          "id",
          "number",
          "tenantId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Phone number id."
          },
          "number": {
            "type": "string",
            "description": "The number in E.164 format, e.g. `+46812345678`."
          },
          "tenantId": {
            "type": "string",
            "description": "Tenant that owns the number."
          },
          "assignedAgentId": {
            "type": "string",
            "description": "Agent answering calls to this number, if assigned.",
            "nullable": true
          },
          "assignedAgentName": {
            "type": "string",
            "description": "Name of that agent.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A phone number registered on the platform."
      },
      "PhoneNumberUpdateDto": {
        "required": [
          "number"
        ],
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "The new number in E.164 format."
          }
        },
        "additionalProperties": false,
        "description": "A changed phone number."
      },
      "PublicConversationDto": {
        "required": [
          "id",
          "tenantId",
          "transcript"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Conversation id."
          },
          "tenantId": {
            "type": "string",
            "description": "Tenant the conversation belongs to."
          },
          "agentId": {
            "type": "string",
            "description": "Agent that handled it; null if the agent was deleted since.",
            "nullable": true
          },
          "agentName": {
            "type": "string",
            "description": "Agent name at the time of the conversation.",
            "nullable": true
          },
          "channel": {
            "type": "string",
            "description": "`phone`, `voice_chat` (web voice, including text chats upgraded to voice) or `text_chat` (web text). Older data may contain `text_voice_chat`.",
            "nullable": true
          },
          "startUtc": {
            "type": "string",
            "description": "Start time (UTC).",
            "format": "date-time",
            "nullable": true
          },
          "endUtc": {
            "type": "string",
            "description": "End time (UTC).",
            "format": "date-time",
            "nullable": true
          },
          "duration": {
            "type": "integer",
            "description": "Length in seconds.",
            "format": "int32",
            "nullable": true
          },
          "endReason": {
            "type": "string",
            "description": "Why the conversation ended (e.g. caller hung up, AI ended the call).",
            "nullable": true
          },
          "agentPhoneNumber": {
            "type": "string",
            "description": "Number the caller dialled (phone), E.164.",
            "nullable": true
          },
          "userPhoneNumber": {
            "type": "string",
            "description": "Caller's number (phone), E.164.",
            "nullable": true
          },
          "summary": {
            "type": "string",
            "description": "Summary generated after the conversation, in the conversation's language.",
            "nullable": true
          },
          "result": {
            "type": "string",
            "description": "Outcome label, if set.",
            "nullable": true
          },
          "topic": {
            "type": "string",
            "description": "Short topic (1–2 words), generated after the conversation.",
            "nullable": true
          },
          "sentiment": {
            "type": "string",
            "description": "Sentiment label, if set.",
            "nullable": true
          },
          "successCriteria": {
            "type": "string",
            "description": "Success criteria evaluation, if set.",
            "nullable": true
          },
          "messages": {
            "type": "integer",
            "description": "Number of messages in `transcript`.",
            "format": "int32"
          },
          "transcript": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicTranscriptMessageDto"
            },
            "description": "What was said, in order: the visitor's or caller's messages and the agent's. The agent's tool calls are not included."
          }
        },
        "additionalProperties": false,
        "description": "A conversation: who talked to which agent, when, what was said and how it ended."
      },
      "PublicConversationListItemDto": {
        "required": [
          "id",
          "tenantId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Conversation id."
          },
          "tenantId": {
            "type": "string",
            "description": "Tenant the conversation belongs to."
          },
          "agentId": {
            "type": "string",
            "description": "Agent that handled it; null if the agent was deleted since.",
            "nullable": true
          },
          "agentName": {
            "type": "string",
            "description": "Agent name at the time of the conversation.",
            "nullable": true
          },
          "channel": {
            "type": "string",
            "description": "`phone`, `voice_chat` (web voice, including text chats upgraded to voice) or `text_chat` (web text). Older data may contain `text_voice_chat`.",
            "nullable": true
          },
          "startUtc": {
            "type": "string",
            "description": "Start time (UTC).",
            "format": "date-time",
            "nullable": true
          },
          "endUtc": {
            "type": "string",
            "description": "End time (UTC).",
            "format": "date-time",
            "nullable": true
          },
          "duration": {
            "type": "integer",
            "description": "Length in seconds.",
            "format": "int32",
            "nullable": true
          },
          "result": {
            "type": "string",
            "description": "Outcome label, if set.",
            "nullable": true
          },
          "topic": {
            "type": "string",
            "description": "Short topic (1–2 words), generated after the conversation.",
            "nullable": true
          },
          "sentiment": {
            "type": "string",
            "description": "Sentiment label, if set.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A conversation in a list."
      },
      "PublicConversationListItemDtoPagedResult": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicConversationListItemDto"
            },
            "description": "The items on this page."
          },
          "page": {
            "type": "integer",
            "description": "Page number, starting at 1.",
            "format": "int32"
          },
          "pageSize": {
            "type": "integer",
            "description": "Items per page.",
            "format": "int32"
          },
          "totalCount": {
            "type": "integer",
            "description": "Total number of items across all pages.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "One page of results."
      },
      "PublicTranscriptMessageDto": {
        "required": [
          "role",
          "text"
        ],
        "type": "object",
        "properties": {
          "role": {
            "enum": [
              "user",
              "agent"
            ],
            "type": "string",
            "description": "`user` (the visitor or caller) or `agent`."
          },
          "text": {
            "type": "string",
            "description": "What was said or written."
          },
          "startTimeMs": {
            "type": "integer",
            "description": "When the message started, in milliseconds from the start of the conversation, if known.",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One message of a conversation's transcript."
      },
      "StatusRequest": {
        "required": [
          "ids"
        ],
        "type": "object",
        "properties": {
          "ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Document ids (as returned by the upload)."
          }
        },
        "additionalProperties": false,
        "description": "Documents to check."
      },
      "TenantCreateDto": {
        "required": [
          "name",
          "region",
          "type"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "type": {
            "enum": [
              "main",
              "sub",
              "customer"
            ],
            "type": "string",
            "description": "Tenant type; must be allowed under the parent's type (see Tenant.type)."
          },
          "domain": {
            "type": "string",
            "description": "Email domain of the organisation, unique across tenants. Optional.",
            "nullable": true
          },
          "extraId": {
            "type": "string",
            "description": "Your own reference for this tenant. Optional.",
            "nullable": true
          },
          "region": {
            "enum": [
              "eu"
            ],
            "type": "string",
            "description": "Data region the new tenant's data will live in. Required; usually the parent's region, but a global company may pick another. Cannot be changed afterwards."
          }
        },
        "additionalProperties": false,
        "description": "A new tenant, created under the tenant in the URL."
      },
      "TenantDto": {
        "required": [
          "id",
          "name",
          "region",
          "type"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Tenant id."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "parentId": {
            "type": "string",
            "description": "Parent tenant id.",
            "nullable": true
          },
          "type": {
            "enum": [
              "main",
              "sub",
              "customer"
            ],
            "type": "string",
            "description": "Tenant type. Determines which child types it may contain: `main` → `main`, `sub`, `customer`; `sub` → `customer`; `customer` has no children."
          },
          "domain": {
            "type": "string",
            "description": "Email domain of the organisation (e.g. `example.com`), unique across tenants. Optional.",
            "nullable": true
          },
          "extraId": {
            "type": "string",
            "description": "Your own reference for this tenant (e.g. a CRM or billing id). Optional, free text.",
            "nullable": true
          },
          "region": {
            "type": "string",
            "description": "Data region the tenant's data lives in (`eu`); chosen when the tenant was created, and carried by its API keys (`evk_eu_...`)."
          }
        },
        "additionalProperties": false,
        "description": "A tenant (organisation) in the tenant hierarchy."
      },
      "TenantMemberDto": {
        "required": [
          "email",
          "firstName",
          "lastName",
          "tenantId",
          "userId"
        ],
        "type": "object",
        "properties": {
          "tenantId": {
            "type": "string",
            "description": "Tenant id."
          },
          "userId": {
            "type": "string",
            "description": "User id; use it to change the role or remove the member."
          },
          "email": {
            "type": "string",
            "description": "The user's email address."
          },
          "firstName": {
            "type": "string",
            "description": "First name."
          },
          "lastName": {
            "type": "string",
            "description": "Last name."
          },
          "role": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantRole"
              }
            ],
            "description": "Role in this tenant. Members of a parent tenant act as admins here."
          }
        },
        "additionalProperties": false,
        "description": "A direct member of a tenant."
      },
      "TenantRole": {
        "enum": [
          "User",
          "Admin",
          "Owner"
        ],
        "type": "string",
        "description": "Role in a tenant. `User`: can work with the tenant's resources. `Admin`: can also manage members and settings. `Owner`: reserved, not assignable through the API."
      },
      "TenantUpdateDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "domain": {
            "type": "string",
            "description": "Email domain, unique across tenants; null or empty to clear.",
            "nullable": true
          },
          "extraId": {
            "type": "string",
            "description": "Your own reference for this tenant; null or empty to clear.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "New values for a tenant. The type cannot be changed."
      },
      "TenantUsageDto": {
        "required": [
          "agents",
          "id",
          "name",
          "totalDirect"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Tenant id."
          },
          "name": {
            "type": "string",
            "description": "Tenant name."
          },
          "extraId": {
            "type": "string",
            "description": "Your own reference for the tenant (e.g. customer number), if set.",
            "nullable": true
          },
          "agents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentUsageDto"
            },
            "description": "Usage per agent and knowledge-base state, directly on this tenant. An agent that had a knowledge base for part of the period appears twice: once with `hasKnowledgeBase` true and once false."
          },
          "totalDirect": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UsageTotalsDto"
              }
            ],
            "description": "Totals of usage directly on this tenant."
          },
          "totalIncludingSubtenants": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UsageTotalsDto"
              }
            ],
            "description": "Totals of this tenant plus all its sub-tenants. Only when sub-tenants are included.",
            "nullable": true
          },
          "subtenants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TenantUsageDto"
            },
            "description": "Sub-tenants with usage in the period, sorted by name; branches without any usage are left out. Only when sub-tenants are included.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Usage of one tenant."
      },
      "TenantUsageReportDto": {
        "required": [
          "period",
          "tenant"
        ],
        "type": "object",
        "properties": {
          "generatedAtUtc": {
            "type": "string",
            "description": "When this report was generated (UTC).",
            "format": "date-time"
          },
          "period": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UsagePeriodDto"
              }
            ],
            "description": "The requested period."
          },
          "includeSubtenants": {
            "type": "boolean",
            "description": "Whether sub-tenants are included."
          },
          "tenant": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantUsageDto"
              }
            ],
            "description": "The requested tenant with its usage, and its sub-tenants when included."
          }
        },
        "additionalProperties": false,
        "description": "Usage of a tenant (and optionally its sub-tenants) in a period, for billing. Voice conversations (`phone`,\n`voice_chat`) are measured in time; text chats (`text_chat`) in input and output tokens. Everything is split by\nwhether the agent had a knowledge base during the conversation."
      },
      "TextUsageDto": {
        "type": "object",
        "properties": {
          "conversations": {
            "type": "integer",
            "description": "Number of text chats.",
            "format": "int64"
          },
          "inputTokens": {
            "type": "integer",
            "description": "Total input text tokens.",
            "format": "int64"
          },
          "outputTokens": {
            "type": "integer",
            "description": "Total output text tokens.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "Text chat usage, billed on tokens."
      },
      "UploadStatusDto": {
        "required": [
          "state"
        ],
        "type": "object",
        "properties": {
          "state": {
            "enum": [
              "processing",
              "complete",
              "error"
            ],
            "type": "string",
            "description": "`processing`, `complete` or `error`."
          },
          "progress": {
            "type": "number",
            "description": "Progress from 0 to 1.",
            "format": "double"
          },
          "fileSizeBytes": {
            "type": "integer",
            "description": "File size in bytes.",
            "format": "int64",
            "nullable": true
          },
          "uploadedUtc": {
            "type": "string",
            "description": "Upload time, ISO 8601 (UTC).",
            "nullable": true
          },
          "fileName": {
            "type": "string",
            "description": "Original file name.",
            "nullable": true
          },
          "errorMessage": {
            "type": "string",
            "description": "Why processing failed, when `state` is `error`.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Processing status of an uploaded document."
      },
      "UsageDto": {
        "required": [
          "text",
          "voice"
        ],
        "type": "object",
        "properties": {
          "voice": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VoiceUsageDto"
              }
            ],
            "description": "Voice usage (phone and web voice)."
          },
          "text": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TextUsageDto"
              }
            ],
            "description": "Text chat usage."
          }
        },
        "additionalProperties": false,
        "description": "Voice and text usage."
      },
      "UsagePeriodDto": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "description": "Start, inclusive (UTC).",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "description": "End, exclusive (UTC).",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A reporting period."
      },
      "UsageTotalsDto": {
        "required": [
          "all",
          "withKnowledgeBase",
          "withoutKnowledgeBase"
        ],
        "type": "object",
        "properties": {
          "all": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UsageDto"
              }
            ],
            "description": "All usage."
          },
          "withKnowledgeBase": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UsageDto"
              }
            ],
            "description": "Usage of agents that had a knowledge base."
          },
          "withoutKnowledgeBase": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UsageDto"
              }
            ],
            "description": "Usage of agents without a knowledge base."
          }
        },
        "additionalProperties": false,
        "description": "Totals, overall and split by knowledge base."
      },
      "VoiceUsageDto": {
        "required": [
          "durationMmss"
        ],
        "type": "object",
        "properties": {
          "conversations": {
            "type": "integer",
            "description": "Number of voice conversations (phone calls and web voice chats).",
            "format": "int64"
          },
          "durationSeconds": {
            "type": "integer",
            "description": "Total duration in seconds. Use this for exact billing.",
            "format": "int64"
          },
          "durationMinutes": {
            "type": "number",
            "description": "Total duration in decimal minutes (seconds ÷ 60, 2 decimals), e.g. 2 min 30 s = 2.5 and 2 min 3 s = 2.05. Multiply by a per-minute price. Computed from this level's own seconds, so agent lines may not add up to the total to the last 0.01.",
            "format": "double"
          },
          "durationMmss": {
            "type": "string",
            "description": "Total duration as minutes:seconds, e.g. `937:35` (minutes are not capped at 60)."
          }
        },
        "additionalProperties": false,
        "description": "Voice usage, billed on time."
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "description": "Tenant API key (evk_…), created in Studio → Vault → API keys.",
        "scheme": "bearer"
      }
    }
  },
  "security": [
    {
      "Bearer": []
    }
  ],
  "tags": [
    {
      "name": "Me",
      "description": "The API key you are calling with and its tenant. Start here."
    },
    {
      "name": "Tenants",
      "description": "Your tenant and its sub-tenants (your customers): list and update them, create a customer under a parent, find one by your own `extraId`."
    },
    {
      "name": "Members & invites",
      "description": "Users of a tenant and their roles; invite a customer's admin."
    },
    {
      "name": "Agents",
      "description": "Voice and chat agents of a tenant: create and configure them, also from templates, and assign knowledge bases and phone numbers."
    },
    {
      "name": "Knowledge bases",
      "description": "Knowledge bases and the documents agents answer from."
    },
    {
      "name": "Phone numbers",
      "description": "Phone numbers of a tenant, which agents answer."
    },
    {
      "name": "Conversations",
      "description": "Conversations with their transcripts and outcomes (read only)."
    },
    {
      "name": "Usage",
      "description": "Usage per tenant and period, for billing (read only)."
    }
  ],
  "externalDocs": {
    "description": "Guides and API reference",
    "url": "https://docs.envocal.ai/api-reference/"
  }
}
