{"openapi":"3.1.0","info":{"title":"Zvani API","version":"1.0.0","description":"API that lets a client's own AI assistant, CRM or script read their Zvani workspace — calls handled by the AI voice agents, per-day statistics and a caller's history — and place outbound calls with its agents. A walkthrough, including connecting over MCP, is at /docs.\n\nGetting a key: a workspace owner or admin creates one in the Zvani web app under Members → API keys. Keys look like `zv_<40 hex chars>`, are scoped to one workspace and carry scopes: `read` (calls, agents, campaigns, usage), `place_calls` (start outbound calls, which spends call minutes), `callbacks` (ring back only people who called before) and `custom_tools` (manage an agent's own tools). Keep them secret; they can be revoked at any time.\n\nSend the key as `Authorization: Bearer zv_...` or `x-api-key: zv_...`. All endpoints return JSON. Errors use `{ \"error\": \"snake_case_code\", \"hint\": \"human/agent-readable explanation\" }`. Rate limit: 120 requests per minute per key, and 60 per minute per IP address for requests that send no key at all, such as this document and the discovery endpoint (HTTP 429 with Retry-After when exceeded). Do not poll for new calls: enable the webhook module in the Zvani web app and react to its events instead.\n\nLists are paginated with an opaque `cursor`; pass `nextCursor` from the previous page until it is null."},"servers":[{"url":"https://zvani.lv"}],"paths":{"/api/v1/":{"get":{"operationId":"meta.discovery","summary":"Discovery: what this API offers and how to authenticate","description":"Start here. No credentials needed. Lists the public operations, where the OpenAPI document is, and how to obtain and send an API key.","parameters":[],"responses":{"200":{"description":"Discovery: what this API offers and how to authenticate","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"},"docs":{"type":"object","properties":{"openapi":{"type":"string"},"openapiInternal":{"type":"string"}},"required":["openapi","openapiInternal"],"additionalProperties":false},"auth":{"type":"object","properties":{"schemes":{"type":"array","items":{"type":"string"}},"howToGetAKey":{"type":"string"}},"required":["schemes","howToGetAKey"],"additionalProperties":false},"operations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"method":{"type":"string"},"path":{"type":"string"},"summary":{"type":"string"}},"required":["id","method","path","summary"],"additionalProperties":false}}},"required":["name","version","docs","auth","operations"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/me":{"get":{"operationId":"me.get","summary":"Who am I: the workspace and access this credential carries","description":"Use it to verify a key works and to learn the workspace id and scopes before doing anything else.","parameters":[],"responses":{"200":{"description":"Who am I: the workspace and access this credential carries","content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["user","apiKey"]},"tenantId":{"type":["string","null"]},"tenant":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"scopes":{"type":"array","items":{"type":"string"}},"keyName":{"type":"string"},"role":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"platformRole":{"type":"string","enum":["superadmin","user"]}},"required":["id","email","platformRole"],"additionalProperties":false},"isSuperadmin":{"type":"boolean"},"emailVerified":{"type":"boolean"},"tenants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"URL-safe short name, e.g. \"acme-clinic\""},"name":{"type":"string"},"status":{"type":"string","enum":["active","suspended","archived"]},"planId":{"type":"string"},"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"createdAt":{"type":"string"}},"required":["id","slug","name","status","settings","createdAt"],"additionalProperties":false}},"memberships":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"tenantId":{"type":"string"},"role":{"type":"string","enum":["owner","admin","manager","viewer"]},"scope":{"anyOf":[{"type":"string","const":"whole_team"},{"type":"object","properties":{"agentIds":{"type":"array","items":{"type":"string"}},"memberIds":{"type":"array","items":{"type":"string"}}},"required":["agentIds","memberIds"],"additionalProperties":false}]},"createdAt":{"type":"string"}},"required":["userId","tenantId","role","scope","createdAt"],"additionalProperties":false}},"legal":{"type":"object","properties":{"version":{"type":"string"},"acceptedVersion":{"type":["string","null"]}},"required":["version","acceptedVersion"],"additionalProperties":false}},"required":["kind","tenantId"],"additionalProperties":false}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public"}},"/api/v1/webhooks":{"post":{"operationId":"webhooks.subscribe","summary":"Subscribe a URL to call events","description":"From now on every event you chose is POSTed to the URL as {\"event\", \"deliveredAt\", ...}, with the record under its own key: \"call\" (the shape of calls.get) for call.* and lead.*, \"campaign\" for campaign.*. Events — call.completed: every finished call, once its summary is ready (at once where no analysis is configured); call.needs_callback: the caller asked to be called back; call.transferred: the call went to a person; lead.updated: a finished call's record changed afterwards: marked reviewed, its callback done, a note added; booking.created: the agent booked an appointment; order_request.created: the agent wrote down an order request; sms.sent: the agent sent the caller a text; campaign.finished: an outbound campaign has no contacts left to ring. Deliveries are signed: X-Zvani-Timestamp (milliseconds) and X-Zvani-Signature, the hex HMAC-SHA256 of \"timestamp.body\" with the secret returned here, and carry X-Zvani-Event-Id and X-Zvani-Delivery-Id so a repeat can be dropped. Answer 2xx; answer 410 to unsubscribe. A 408, 429, 5xx or no answer is retried after 1 min, 5 min, 30 min, 2 h and 6 h; any other 3xx or 4xx is final; a day of nothing but failures disables the subscription. Subscribing a URL this caller already subscribed answers that subscription (its events replaced by the ones sent now, and switched back on if it was disabled) instead of a second one. https URLs on public hosts only.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":10,"description":"https URL that receives the deliveries"},"events":{"default":["call.completed"],"description":"Which events to send","minItems":1,"type":"array","items":{"type":"string","enum":["call.completed","call.needs_callback","call.transferred","lead.updated","booking.created","order_request.created","sms.sent","campaign.finished"]}},"source":{"description":"A label for who subscribed, e.g. \"zapier\", \"make\", \"n8n\"","type":"string","maxLength":40}},"required":["url"]}}}},"responses":{"201":{"description":"Subscribe a URL to call events","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["call.completed","call.needs_callback","call.transferred","lead.updated","booking.created","order_request.created","sms.sent","campaign.finished"]}},"source":{"description":"Who made it, e.g. \"zapier\"","type":"string"},"createdAt":{"type":"string"},"lastDeliveryAt":{"type":"string"},"lastStatus":{"description":"HTTP status of the last delivery","type":"number"},"failingSince":{"description":"Since when every delivery has failed; absent while deliveries land","type":"string"},"disabledAt":{"description":"Set when a day of failed deliveries switched the subscription off; subscribing the same URL again switches it back on","type":"string"},"secret":{"type":"string","description":"Verify X-Zvani-Signature with this; it is not shown again"}},"required":["id","url","events","createdAt","secret"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation; The caller is a member without modules.configure; owners, admins and API keys may subscribe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The workspace already has 50 subscriptions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a public https URL; details.reason says why","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"},"get":{"operationId":"webhooks.list","summary":"The webhook subscriptions of this workspace","description":"Secrets are not shown. An owner or admin sees every subscription; a key or another member sees the ones it made.","parameters":[],"responses":{"200":{"description":"The webhook subscriptions of this workspace","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["call.completed","call.needs_callback","call.transferred","lead.updated","booking.created","order_request.created","sms.sent","campaign.finished"]}},"source":{"description":"Who made it, e.g. \"zapier\"","type":"string"},"createdAt":{"type":"string"},"lastDeliveryAt":{"type":"string"},"lastStatus":{"description":"HTTP status of the last delivery","type":"number"},"failingSince":{"description":"Since when every delivery has failed; absent while deliveries land","type":"string"},"disabledAt":{"description":"Set when a day of failed deliveries switched the subscription off; subscribing the same URL again switches it back on","type":"string"}},"required":["id","url","events","createdAt"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/webhooks/{id}":{"delete":{"operationId":"webhooks.unsubscribe","summary":"Remove a webhook subscription","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Remove a webhook subscription","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation; The caller is a member without modules.configure; owners, admins and API keys may subscribe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/calls":{"get":{"operationId":"calls.list","summary":"Calls handled by the AI agents, newest first, paginated","description":"Defaults to the last 30 days. Filter by from/to (ISO 8601), direction, agentId or status. Follow nextCursor for older calls. Transcripts are not included; each item carries the AI summary, score and follow-ups.","parameters":[{"name":"from","in":"query","required":false,"description":"Start, inclusive; default 30 days ago","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End, inclusive; a date covers that whole day; default now","schema":{"type":"string"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string","enum":["inbound","outbound"]}},{"name":"agentId","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["in_progress","completed_action","incomplete_call","transferred","failed"]}},{"name":"limit","in":"query","required":false,"description":"Page size, 1–200","schema":{"default":50,"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page","schema":{"type":"string"}}],"responses":{"200":{"description":"Calls handled by the AI agents, newest first, paginated","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"agentId":{"type":"string","description":"The AI voice agent that handled the call"},"direction":{"type":"string","enum":["inbound","outbound"]},"from":{"type":"string","description":"Caller number, E.164 where known"},"to":{"type":"string","description":"Called number"},"status":{"type":"string","enum":["in_progress","completed_action","incomplete_call","transferred","failed"]},"startedAt":{"type":"string"},"durationSeconds":{"type":"number"},"summary":{"description":"AI summary in the workspace language","type":"string"},"sentiment":{"type":"string"},"score":{"description":"Overall quality score, 0–100","type":"number"},"keyFacts":{"type":"array","items":{"type":"string"}},"nextSteps":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"date":{"type":"string"},"priority":{"type":"string"}},"required":["task","date","priority"],"additionalProperties":false}},"clientName":{"type":"string"},"companyName":{"type":"string"},"needsCallback":{"type":"boolean"},"transferredTo":{"description":"Set when the call was handed to a human","type":"string"},"handoffs":{"description":"Each time the call was passed to another assistant of the workspace, oldest first","type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"toAgentId":{"type":"string"},"toName":{"type":"string"},"reason":{"type":"string"},"summary":{"type":"string"}},"required":["at","toAgentId","toName"],"additionalProperties":false}},"analysedAt":{"type":"string"},"outcome":{"description":"What the agent's outcome tool recorded, as the agent passed it","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"outcomeTool":{"description":"The tool that recorded `outcome`, e.g. log_call","type":"string"},"outcomeSource":{"description":"agent: the agent called its outcome tool on the call; transcript: filled from the transcript afterwards because it did not","type":"string","enum":["agent","transcript"]},"campaignId":{"description":"The campaign an outbound call belongs to","type":"string"},"placementId":{"description":"For a call placed with calls.place: the id calls.wait takes","type":"string"}},"required":["id","agentId","direction","from","to","status","startedAt","durationSeconds","needsCallback"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/calls/daily-stats":{"get":{"operationId":"calls.dailyStats","summary":"Per-day call counts, transfers, average score and talk time","description":"Give from/to, or days (1–90, default 30). Day boundaries follow tz (IANA) or the workspace timezone, and a date-only from/to covers that whole local day. The range may cover at most 90 days. Quiet days are included with zeros.","parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":90}},{"name":"tz","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Per-day call counts, transfers, average score and talk time","content":{"application/json":{"schema":{"type":"object","properties":{"timezone":{"type":"string"},"days":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"calls":{"type":"number"},"transferred":{"type":"number"},"avgScore":{"type":["number","null"]},"seconds":{"type":"number"}},"required":["day","calls","transferred","avgScore","seconds"],"additionalProperties":false}}},"required":["timezone","days"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/calls/{id}":{"get":{"operationId":"calls.get","summary":"One call by id","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"One call by id","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"agentId":{"type":"string","description":"The AI voice agent that handled the call"},"direction":{"type":"string","enum":["inbound","outbound"]},"from":{"type":"string","description":"Caller number, E.164 where known"},"to":{"type":"string","description":"Called number"},"status":{"type":"string","enum":["in_progress","completed_action","incomplete_call","transferred","failed"]},"startedAt":{"type":"string"},"durationSeconds":{"type":"number"},"summary":{"description":"AI summary in the workspace language","type":"string"},"sentiment":{"type":"string"},"score":{"description":"Overall quality score, 0–100","type":"number"},"keyFacts":{"type":"array","items":{"type":"string"}},"nextSteps":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"date":{"type":"string"},"priority":{"type":"string"}},"required":["task","date","priority"],"additionalProperties":false}},"clientName":{"type":"string"},"companyName":{"type":"string"},"needsCallback":{"type":"boolean"},"transferredTo":{"description":"Set when the call was handed to a human","type":"string"},"handoffs":{"description":"Each time the call was passed to another assistant of the workspace, oldest first","type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"toAgentId":{"type":"string"},"toName":{"type":"string"},"reason":{"type":"string"},"summary":{"type":"string"}},"required":["at","toAgentId","toName"],"additionalProperties":false}},"analysedAt":{"type":"string"},"outcome":{"description":"What the agent's outcome tool recorded, as the agent passed it","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"outcomeTool":{"description":"The tool that recorded `outcome`, e.g. log_call","type":"string"},"outcomeSource":{"description":"agent: the agent called its outcome tool on the call; transcript: filled from the transcript afterwards because it did not","type":"string","enum":["agent","transcript"]},"campaignId":{"description":"The campaign an outbound call belongs to","type":"string"},"placementId":{"description":"For a call placed with calls.place: the id calls.wait takes","type":"string"}},"required":["id","agentId","direction","from","to","status","startedAt","durationSeconds","needsCallback"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/calls/{id}/reviews":{"get":{"operationId":"callReviews.forCall","summary":"Remarks on how the agent handled one call","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Remarks on how the agent handled one call","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"callId":{"type":"string"},"agentId":{"type":"string"},"author":{"type":"string"},"kind":{"type":"string","enum":["mistake","improve","good"],"description":"mistake: done wrong; improve: could be better; good: done well"},"body":{"type":"string"},"createdAt":{"type":"string"},"mine":{"type":"boolean","description":"Written by whoever is asking, who may therefore delete it"},"status":{"type":"string","enum":["open","applied","dismissed"],"description":"open: not acted on yet; applied: the prompt was changed for it; dismissed: nothing to change"},"handledAt":{"type":"string"},"handledBy":{"type":"string"},"handledNote":{"description":"What was changed because of it, or why nothing was","type":"string"},"clientId":{"description":"The id the sending app gave it, echoed back; see POST","type":"string"}},"required":["id","callId","author","kind","body","createdAt","mine","status"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No such call (or remark) in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no remarks: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"},"post":{"operationId":"callReviews.add","summary":"Add a remark on how the agent handled a call","description":"What it did wrong, what it could do better, or what it did well, in a sentence or a few. Remarks are listed across the workspace with GET /call-reviews when the prompt is improved.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["mistake","improve","good"]},"body":{"type":"string","minLength":1,"maxLength":4000},"clientId":{"description":"Your own id for this remark. Send the same one again if the first answer was lost: the remark is stored once and the existing row comes back.","type":"string","minLength":1,"maxLength":80}},"required":["kind","body"]}}}},"responses":{"200":{"description":"Add a remark on how the agent handled a call","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"callId":{"type":"string"},"agentId":{"type":"string"},"author":{"type":"string"},"kind":{"type":"string","enum":["mistake","improve","good"],"description":"mistake: done wrong; improve: could be better; good: done well"},"body":{"type":"string"},"createdAt":{"type":"string"},"mine":{"type":"boolean","description":"Written by whoever is asking, who may therefore delete it"},"status":{"type":"string","enum":["open","applied","dismissed"],"description":"open: not acted on yet; applied: the prompt was changed for it; dismissed: nothing to change"},"handledAt":{"type":"string"},"handledBy":{"type":"string"},"handledNote":{"description":"What was changed because of it, or why nothing was","type":"string"},"clientId":{"description":"The id the sending app gave it, echoed back; see POST","type":"string"}},"required":["id","callId","author","kind","body","createdAt","mine","status"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No such call (or remark) in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no remarks: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.annotate"}},"/api/v1/calls/{id}/reviews/{reviewId}":{"delete":{"operationId":"callReviews.remove","summary":"Delete a remark","description":"Its author may delete it, and so may the workspace’s owners and admins.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"reviewId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Delete a remark","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","const":true}},"required":["deleted"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation; Someone else’s remark, and you are not an owner or admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No such call (or remark) in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no remarks: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.annotate"},"patch":{"operationId":"callReviews.setStatus","summary":"Mark a remark applied, dismissed, or open again","description":"A remark changes nothing by itself. Whoever improves the prompt reads the open remarks (GET /call-reviews?status=open), changes the prompt, and marks each one applied with a note of what changed, or dismissed when there is nothing to change; the next round then reads only new ones.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"reviewId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","applied","dismissed"]},"note":{"description":"What was changed because of it, or why nothing was","type":"string","maxLength":2000}},"required":["status"]}}}},"responses":{"200":{"description":"Mark a remark applied, dismissed, or open again","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"callId":{"type":"string"},"agentId":{"type":"string"},"author":{"type":"string"},"kind":{"type":"string","enum":["mistake","improve","good"],"description":"mistake: done wrong; improve: could be better; good: done well"},"body":{"type":"string"},"createdAt":{"type":"string"},"mine":{"type":"boolean","description":"Written by whoever is asking, who may therefore delete it"},"status":{"type":"string","enum":["open","applied","dismissed"],"description":"open: not acted on yet; applied: the prompt was changed for it; dismissed: nothing to change"},"handledAt":{"type":"string"},"handledBy":{"type":"string"},"handledNote":{"description":"What was changed because of it, or why nothing was","type":"string"},"clientId":{"description":"The id the sending app gave it, echoed back; see POST","type":"string"}},"required":["id","callId","author","kind","body","createdAt","mine","status"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No such call (or remark) in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no remarks: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"agents.edit"}},"/api/v1/call-reviews":{"get":{"operationId":"callReviews.list","summary":"Remarks on calls across the workspace, newest first","parameters":[{"name":"from","in":"query","required":false,"description":"Remarks made from, inclusive; default 30 days ago","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"Remarks made until, inclusive; a date covers that whole day; default now","schema":{"type":"string"}},{"name":"agentId","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Only remarks in this state; open gives what has not been acted on yet","schema":{"type":"string","enum":["open","applied","dismissed"]}}],"responses":{"200":{"description":"Remarks on calls across the workspace, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"callId":{"type":"string"},"agentId":{"type":"string"},"author":{"type":"string"},"kind":{"type":"string","enum":["mistake","improve","good"],"description":"mistake: done wrong; improve: could be better; good: done well"},"body":{"type":"string"},"createdAt":{"type":"string"},"mine":{"type":"boolean","description":"Written by whoever is asking, who may therefore delete it"},"status":{"type":"string","enum":["open","applied","dismissed"],"description":"open: not acted on yet; applied: the prompt was changed for it; dismissed: nothing to change"},"handledAt":{"type":"string"},"handledBy":{"type":"string"},"handledNote":{"description":"What was changed because of it, or why nothing was","type":"string"},"clientId":{"description":"The id the sending app gave it, echoed back; see POST","type":"string"},"call":{"anyOf":[{"type":"object","properties":{"startedAt":{"type":"string"},"direction":{"type":"string","enum":["inbound","outbound"]},"customer":{"type":"string"},"agentName":{"type":"string"},"summary":{"type":"string"}},"required":["startedAt","direction","customer"],"additionalProperties":false},{"type":"null"}],"description":"Null when the call is no longer readable"}},"required":["id","callId","author","kind","body","createdAt","mine","status","call"],"additionalProperties":false}},"truncated":{"type":"boolean"}},"required":["items","truncated"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no remarks: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/knowledge/pages":{"get":{"operationId":"knowledge.pages.list","summary":"Pages kept in the knowledge base by another system","description":"The documents that carry an external id, with their indexing status.","parameters":[],"responses":{"200":{"description":"Pages kept in the knowledge base by another system","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["pdf","docx","text","url"]},"url":{"type":"string"},"externalId":{"description":"The id another system keeps this page under, e.g. \"wp:42\"","type":"string"},"sizeBytes":{"type":"number"},"status":{"type":"string","enum":["indexing","ready","failed"]},"error":{"type":"string"},"chunks":{"type":"number","description":"Pieces the document was cut into"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","kind","sizeBytes","status","chunks","createdAt","updatedAt"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"}},"/api/v1/knowledge/pages/{externalId}":{"put":{"operationId":"knowledge.pages.put","summary":"Put a page into the knowledge base, or replace it","description":"The page is addressed by the sending system’s own id (e.g. \"wp:42\"). Send its title, its text (plain text or Markdown, up to 1 MB) and, for reference, its public URL. A page with the same id is replaced and indexed again; the agent finds it within a minute. Switches the knowledge base on for the workspace if it was off. At most 120 pages a minute from one address: a 429 says in its Retry-After header how many seconds to wait before sending the next page, so a first sync of a large site paces itself rather than failing.","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{1,120}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":300},"text":{"type":"string","minLength":1,"maxLength":1000000},"url":{"type":"string","maxLength":2000,"format":"uri"}},"required":["title","text"]}}}},"responses":{"202":{"description":"Put a page into the knowledge base, or replace it","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["pdf","docx","text","url"]},"url":{"type":"string"},"externalId":{"description":"The id another system keeps this page under, e.g. \"wp:42\"","type":"string"},"sizeBytes":{"type":"number"},"status":{"type":"string","enum":["indexing","ready","failed"]},"error":{"type":"string"},"chunks":{"type":"number","description":"Pieces the document was cut into"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","kind","sizeBytes","status","chunks","createdAt","updatedAt"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The id starts with \"site:\", which the website sync keeps for its own pages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"},"delete":{"operationId":"knowledge.pages.delete","summary":"Take a page out of the knowledge base","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{1,120}$"}}],"responses":{"200":{"description":"Take a page out of the knowledge base","content":{"application/json":{"schema":{"type":"object","properties":{"externalId":{"type":"string"},"deleted":{"type":"boolean"}},"required":["externalId","deleted"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"}},"/api/v1/knowledge/sites":{"get":{"operationId":"knowledge.sites.list","summary":"Websites kept in the knowledge base","description":"Each site with how many pages it keeps, its status, when it was last read, when it is read next and what the last sync did.","parameters":[],"responses":{"200":{"description":"Websites kept in the knowledge base","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"include":{"type":"string","description":"Path prefixes to keep, one per line; empty keeps every page"},"exclude":{"type":"string","description":"Path prefixes to leave out, one per line"},"maxPages":{"type":"number","description":"Pages the plan lets this site keep"},"frequency":{"type":"string","enum":["daily","weekly"]},"status":{"type":"string","enum":["idle","running","failed"]},"pages":{"type":"number","description":"Pages of this site in the knowledge base now"},"lastStartedAt":{"type":"string"},"lastFinishedAt":{"type":"string"},"nextRunAt":{"type":"string"},"lastResult":{"type":"object","properties":{"found":{"type":"number"},"added":{"type":"number"},"updated":{"type":"number"},"unchanged":{"type":"number"},"removed":{"type":"number"},"skipped":{"type":"number"},"failed":{"type":"number"},"error":{"type":"string"}},"required":["found","added","updated","unchanged","removed","skipped","failed"],"additionalProperties":false},"createdAt":{"type":"string"}},"required":["id","url","include","exclude","maxPages","frequency","status","pages","createdAt"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"},"post":{"operationId":"knowledge.sites.add","summary":"Add a website: its pages are found and kept in the knowledge base","description":"Give the public https address of the site. Its pages are found through robots.txt and sitemaps, or by following links when it has none, and each becomes a document the assistant answers from; the first sync starts at once and the site is read again daily or weekly. include and exclude are path prefixes, one per line. The plan sets how many pages a site may keep. Up to three sites per workspace.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"include":{"type":"string","maxLength":2000},"exclude":{"type":"string","maxLength":2000},"frequency":{"type":"string","enum":["daily","weekly"]}},"required":["url"]}}}},"responses":{"201":{"description":"Add a website: its pages are found and kept in the knowledge base","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"include":{"type":"string","description":"Path prefixes to keep, one per line; empty keeps every page"},"exclude":{"type":"string","description":"Path prefixes to leave out, one per line"},"maxPages":{"type":"number","description":"Pages the plan lets this site keep"},"frequency":{"type":"string","enum":["daily","weekly"]},"status":{"type":"string","enum":["idle","running","failed"]},"pages":{"type":"number","description":"Pages of this site in the knowledge base now"},"lastStartedAt":{"type":"string"},"lastFinishedAt":{"type":"string"},"nextRunAt":{"type":"string"},"lastResult":{"type":"object","properties":{"found":{"type":"number"},"added":{"type":"number"},"updated":{"type":"number"},"unchanged":{"type":"number"},"removed":{"type":"number"},"skipped":{"type":"number"},"failed":{"type":"number"},"error":{"type":"string"}},"required":["found","added","updated","unchanged","removed","skipped","failed"],"additionalProperties":false},"createdAt":{"type":"string"}},"required":["id","url","include","exclude","maxPages","frequency","status","pages","createdAt"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The address is not an https:// web address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The workspace already has three website sources; A source with this address exists in the workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The address points at a private or unresolvable host","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"}},"/api/v1/knowledge/sites/{id}":{"patch":{"operationId":"knowledge.sites.update","summary":"Change a website source: its address, prefixes or how often it is read","description":"Only the fields sent change. A new address or new prefixes take effect at the next sync, which also removes pages that fall out of scope.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"include":{"type":"string","maxLength":2000},"exclude":{"type":"string","maxLength":2000},"frequency":{"type":"string","enum":["daily","weekly"]}}}}}},"responses":{"200":{"description":"Change a website source: its address, prefixes or how often it is read","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"include":{"type":"string","description":"Path prefixes to keep, one per line; empty keeps every page"},"exclude":{"type":"string","description":"Path prefixes to leave out, one per line"},"maxPages":{"type":"number","description":"Pages the plan lets this site keep"},"frequency":{"type":"string","enum":["daily","weekly"]},"status":{"type":"string","enum":["idle","running","failed"]},"pages":{"type":"number","description":"Pages of this site in the knowledge base now"},"lastStartedAt":{"type":"string"},"lastFinishedAt":{"type":"string"},"nextRunAt":{"type":"string"},"lastResult":{"type":"object","properties":{"found":{"type":"number"},"added":{"type":"number"},"updated":{"type":"number"},"unchanged":{"type":"number"},"removed":{"type":"number"},"skipped":{"type":"number"},"failed":{"type":"number"},"error":{"type":"string"}},"required":["found","added","updated","unchanged","removed","skipped","failed"],"additionalProperties":false},"createdAt":{"type":"string"}},"required":["id","url","include","exclude","maxPages","frequency","status","pages","createdAt"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The address is not an https:// web address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No website source with that id in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A source with this address exists in the workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The address points at a private or unresolvable host","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"},"delete":{"operationId":"knowledge.sites.remove","summary":"Remove a website source and every page it put in the knowledge base","description":"The pages and everything indexed from them go with it; documents added by hand or by a plugin stay.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Remove a website source and every page it put in the knowledge base","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","const":true}},"required":["deleted"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No website source with that id in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"}},"/api/v1/knowledge/sites/{id}/run":{"post":{"operationId":"knowledge.sites.run","summary":"Read a website again now","description":"Refused when the last sync started less than ten minutes ago; the schedule runs it anyway.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Read a website again now","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"include":{"type":"string","description":"Path prefixes to keep, one per line; empty keeps every page"},"exclude":{"type":"string","description":"Path prefixes to leave out, one per line"},"maxPages":{"type":"number","description":"Pages the plan lets this site keep"},"frequency":{"type":"string","enum":["daily","weekly"]},"status":{"type":"string","enum":["idle","running","failed"]},"pages":{"type":"number","description":"Pages of this site in the knowledge base now"},"lastStartedAt":{"type":"string"},"lastFinishedAt":{"type":"string"},"nextRunAt":{"type":"string"},"lastResult":{"type":"object","properties":{"found":{"type":"number"},"added":{"type":"number"},"updated":{"type":"number"},"unchanged":{"type":"number"},"removed":{"type":"number"},"skipped":{"type":"number"},"failed":{"type":"number"},"error":{"type":"string"}},"required":["found","added","updated","unchanged","removed","skipped","failed"],"additionalProperties":false},"createdAt":{"type":"string"}},"required":["id","url","include","exclude","maxPages","frequency","status","pages","createdAt"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No website source with that id in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds; The last sync started less than ten minutes ago; details carry retryAfterSeconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install keeps no knowledge index: it runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"knowledge.manage"}},"/api/v1/contacts/{number}":{"get":{"operationId":"contacts.get","summary":"Everything the workspace knows about one phone number","description":"Cross-call history for a customer: how many times they called or were called, when first and last, and their most recent calls with summaries. Use it before calling someone back. Accepts E.164 with or without the leading +.","parameters":[{"name":"number","in":"path","required":true,"schema":{"type":"string","pattern":"^\\+?[0-9]{6,15}$"}}],"responses":{"200":{"description":"Everything the workspace knows about one phone number","content":{"application/json":{"schema":{"type":"object","properties":{"number":{"type":"string"},"callCount":{"type":"number"},"firstSeenAt":{"type":"string"},"lastSeenAt":{"type":"string"},"recentCalls":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"agentId":{"type":"string","description":"The AI voice agent that handled the call"},"direction":{"type":"string","enum":["inbound","outbound"]},"from":{"type":"string","description":"Caller number, E.164 where known"},"to":{"type":"string","description":"Called number"},"status":{"type":"string","enum":["in_progress","completed_action","incomplete_call","transferred","failed"]},"startedAt":{"type":"string"},"durationSeconds":{"type":"number"},"summary":{"description":"AI summary in the workspace language","type":"string"},"sentiment":{"type":"string"},"score":{"description":"Overall quality score, 0–100","type":"number"},"keyFacts":{"type":"array","items":{"type":"string"}},"nextSteps":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"date":{"type":"string"},"priority":{"type":"string"}},"required":["task","date","priority"],"additionalProperties":false}},"clientName":{"type":"string"},"companyName":{"type":"string"},"needsCallback":{"type":"boolean"},"transferredTo":{"description":"Set when the call was handed to a human","type":"string"},"handoffs":{"description":"Each time the call was passed to another assistant of the workspace, oldest first","type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"toAgentId":{"type":"string"},"toName":{"type":"string"},"reason":{"type":"string"},"summary":{"type":"string"}},"required":["at","toAgentId","toName"],"additionalProperties":false}},"analysedAt":{"type":"string"},"outcome":{"description":"What the agent's outcome tool recorded, as the agent passed it","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"outcomeTool":{"description":"The tool that recorded `outcome`, e.g. log_call","type":"string"},"outcomeSource":{"description":"agent: the agent called its outcome tool on the call; transcript: filled from the transcript afterwards because it did not","type":"string","enum":["agent","transcript"]},"campaignId":{"description":"The campaign an outbound call belongs to","type":"string"},"placementId":{"description":"For a call placed with calls.place: the id calls.wait takes","type":"string"}},"required":["id","agentId","direction","from","to","status","startedAt","durationSeconds","needsCallback"],"additionalProperties":false}}},"required":["number","callCount","firstSeenAt","lastSeenAt","recentCalls"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/auth/providers":{"get":{"operationId":"auth.providers","summary":"Which sign-in methods this install offers","description":"For the sign-in screen of the web app. All three are false on an install with no database, which has nobody to sign in; the OAuth providers also depend on configuration.","parameters":[],"responses":{"200":{"description":"Which sign-in methods this install offers","content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"boolean"},"google":{"type":"boolean"},"microsoft":{"type":"boolean"},"passwordReset":{"type":"boolean","description":"Whether a forgotten password can be reset by email"}},"required":["password","google","microsoft","passwordReset"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/register":{"post":{"operationId":"auth.register","summary":"Create an account and its first workspace","description":"For people signing up in the web app. The first account on an install becomes the platform superadmin.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","minLength":3},"password":{"type":"string","minLength":8},"name":{"type":"string"},"companyName":{"type":"string"},"language":{"description":"Prose name, e.g. \"Latvian\"; the workspace default","type":"string"},"acceptTerms":{"type":"boolean","const":true,"description":"The person accepts the Terms (with the Data Processing Agreement and Acceptable Use Policy) and the Privacy Policy, version 2026-10-17"}},"required":["email","password","acceptTerms"]}}}},"responses":{"201":{"description":"Create an account and its first workspace","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"platformRole":{"type":"string","enum":["superadmin","user"]}},"required":["id","email","platformRole"],"additionalProperties":false},"tenant":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"URL-safe short name, e.g. \"acme-clinic\""},"name":{"type":"string"},"status":{"type":"string","enum":["active","suspended","archived"]},"planId":{"type":"string"},"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"createdAt":{"type":"string"}},"required":["id","slug","name","status","settings","createdAt"],"additionalProperties":false},"isSuperadmin":{"type":"boolean"},"verificationSent":{"type":"boolean","description":"A letter with a confirmation link went to the address; the account works meanwhile, but stays unconfirmed until the link is opened"}},"required":["token","user","tenant","isSuperadmin","verificationSent"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"An account with this email already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/verify-email":{"post":{"operationId":"auth.verifyEmail","summary":"Confirm the sign-up address with the link from the letter","description":"The link works once and for a day. Confirming lets colleagues add the address to their workspace and lets a Google or Microsoft sign-in with the same address reach the account.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":10}},"required":["token"]}}}},"responses":{"200":{"description":"Confirm the sign-up address with the link from the letter","content":{"application/json":{"schema":{"type":"object","properties":{"verified":{"type":"boolean"}},"required":["verified"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The link is unknown, already used or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/login":{"post":{"operationId":"auth.login","summary":"Sign in with email and password","description":"For people signing in to the web app. Returns a session token; API keys are the way for programs.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"]}}}},"responses":{"200":{"description":"Sign in with email and password","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"platformRole":{"type":"string","enum":["superadmin","user"]}},"required":["id","email","platformRole"],"additionalProperties":false},"memberships":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"tenantId":{"type":"string"},"role":{"type":"string","enum":["owner","admin","manager","viewer"]},"scope":{"anyOf":[{"type":"string","const":"whole_team"},{"type":"object","properties":{"agentIds":{"type":"array","items":{"type":"string"}},"memberIds":{"type":"array","items":{"type":"string"}}},"required":["agentIds","memberIds"],"additionalProperties":false}]},"createdAt":{"type":"string"}},"required":["userId","tenantId","role","scope","createdAt"],"additionalProperties":false}}},"required":["token","user","memberships"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unknown email or wrong password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/logout":{"post":{"operationId":"auth.logout","summary":"End the browser session","description":"Clears the session cookie. Safe to call without a session; a token-only client just drops its token.","parameters":[],"responses":{"200":{"description":"End the browser session","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/agents":{"get":{"operationId":"agents.list","summary":"List the AI voice agents in this workspace","description":"Start here to learn an agent id, which every other agent operation takes. A member with limited visibility sees only the agents they were given.","parameters":[],"responses":{"200":{"description":"List the AI voice agents in this workspace","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Display name, e.g. \"Anete · reception\""},"didNumber":{"description":"The number this agent answers on, E.164","type":"string"},"direction":{"type":"string","enum":["inbound","outbound"]},"voice":{"type":"string","description":"Gemini voice name, e.g. \"Kore\""},"spokenLanguage":{"type":"string","description":"Prose name, e.g. \"Lithuanian\"; the language it opens the call in"},"summaryLanguage":{"type":"string","description":"Prose name, e.g. \"Latvian\"; the language the summary and outcome are written in"},"alsoSpeaks":{"type":"array","items":{"type":"string"},"description":"Languages it may follow a caller into"},"enabled":{"type":"boolean"},"transferNumber":{"description":"Where a call goes when the caller asks for a person","type":"string"},"handoffRole":{"description":"What this assistant handles, for the workspace's other assistants; set, any of them can hand it a live call","type":"string"},"placeholders":{"type":"array","items":{"type":"string"},"description":"The {placeholders} in the prompt a caller fills per call with calls.place"}},"required":["id","name","direction","voice","spokenLanguage","summaryLanguage","alsoSpeaks","enabled","placeholders"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"agents.view"}},"/api/v1/agents/{id}":{"get":{"operationId":"agents.get","summary":"One agent by id","description":"Who the agent is and where it answers. The prompt, tools, integrations and calendar are not here; a member of the workspace reads those from agents.details.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"One agent by id","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Display name, e.g. \"Anete · reception\""},"didNumber":{"description":"The number this agent answers on, E.164","type":"string"},"direction":{"type":"string","enum":["inbound","outbound"]},"voice":{"type":"string","description":"Gemini voice name, e.g. \"Kore\""},"spokenLanguage":{"type":"string","description":"Prose name, e.g. \"Lithuanian\"; the language it opens the call in"},"summaryLanguage":{"type":"string","description":"Prose name, e.g. \"Latvian\"; the language the summary and outcome are written in"},"alsoSpeaks":{"type":"array","items":{"type":"string"},"description":"Languages it may follow a caller into"},"enabled":{"type":"boolean"},"transferNumber":{"description":"Where a call goes when the caller asks for a person","type":"string"},"handoffRole":{"description":"What this assistant handles, for the workspace's other assistants; set, any of them can hand it a live call","type":"string"},"placeholders":{"type":"array","items":{"type":"string"},"description":"The {placeholders} in the prompt a caller fills per call with calls.place"}},"required":["id","name","direction","voice","spokenLanguage","summaryLanguage","alsoSpeaks","enabled","placeholders"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"agents.view"}},"/api/v1/usage":{"get":{"operationId":"usage.get","summary":"Metered usage for this workspace over a recent window","description":"One row per kind and unit (call seconds, AI tokens, SMS sent…), summed since `periodDays` ago. A kind never recorded is simply absent.","parameters":[{"name":"days","in":"query","required":false,"description":"How far back to sum usage, 1–365","schema":{"default":30,"type":"integer","minimum":1,"maximum":365}}],"responses":{"200":{"description":"Metered usage for this workspace over a recent window","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"unit":{"type":"string"},"total":{"type":"number"}},"required":["kind","unit","total"],"additionalProperties":false}},"periodDays":{"type":"number"}},"required":["items","periodDays"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database, so there is no usage or billing to read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"billing.view"}},"/api/v1/public/plans":{"get":{"operationId":"billing.publicPlans","summary":"The plans on sale, for the website","description":"What the landing page shows: each plan on sale with its monthly price, minutes, price of a further minute, numbers and team size. Plans retired from sale are left out.","parameters":[],"responses":{"200":{"description":"The plans on sale, for the website","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"monthlyPriceEur":{"type":"number"},"includedMinutes":{"type":"number","description":"Zero means unlimited"},"includedTransferMinutes":{"type":["number","null"]},"overagePerMinuteEur":{"type":"number"},"includedNumbers":{"type":"number"},"extraNumberEur":{"type":"number"},"maxAgents":{"type":"number","description":"Zero means unlimited"},"maxMembers":{"type":"number","description":"Zero means unlimited"},"allModules":{"type":"boolean"}},"required":["id","name","monthlyPriceEur","includedMinutes","includedTransferMinutes","overagePerMinuteEur","includedNumbers","extraNumberEur","maxAgents","maxMembers","allModules"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database, so there is no usage or billing to read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/email/confirm":{"post":{"operationId":"auth.confirmEmail","summary":"Confirm a new sign-in address from the emailed link","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":10}},"required":["token"]}}}},"responses":{"200":{"description":"Confirm a new sign-in address from the emailed link","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The link is unknown, already used or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another account took the address meanwhile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/password/forgot":{"post":{"operationId":"auth.forgotPassword","summary":"Email a password reset link","description":"Answers the same whether or not the address has an account.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":200},"lang":{"description":"Language of the letter","type":"string","enum":["en","ru","lv","lt","et"]}},"required":["email"]}}}},"responses":{"200":{"description":"Email a password reset link","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"}},"required":["sent"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database; No mail service is configured, so no letter can be sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/password/reset":{"post":{"operationId":"auth.resetPassword","summary":"Set a new password from the emailed link","description":"Every session the account had ends; sign in with the new password.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":10},"newPassword":{"type":"string","minLength":8,"maxLength":200}},"required":["token","newPassword"]}}}},"responses":{"200":{"description":"Set a new password from the emailed link","content":{"application/json":{"schema":{"type":"object","properties":{"reset":{"type":"boolean"}},"required":["reset"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The link is unknown, already used or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/auth/invite/accept":{"post":{"operationId":"auth.acceptInvite","summary":"Accept a workspace invitation by choosing a password","description":"The link from the invitation letter works once. Answers a session for the workspace the invitation opens.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":10},"newPassword":{"type":"string","minLength":8,"maxLength":200},"name":{"type":"string","maxLength":120}},"required":["token","newPassword"]}}}},"responses":{"200":{"description":"Accept a workspace invitation by choosing a password","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"tenantId":{"type":"string"}},"required":["token","tenantId"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The link is unknown, already used or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The install runs without a database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public"}},"/api/v1/campaigns":{"get":{"operationId":"campaigns.list","summary":"List outbound campaigns in this workspace","description":"Start here to learn a campaign id. `stats` says how far the queue has got; the contact list and the prompt are behind campaigns.details.","parameters":[{"name":"type","in":"query","required":false,"description":"Default: every campaign that is not a template","schema":{"type":"string","enum":["templates","briefings","calls"]}},{"name":"q","in":"query","required":false,"description":"Free text over the name, kind and number it calls from; a signed-in member also searches the contact rows","schema":{"type":"string"}}],"responses":{"200":{"description":"List outbound campaigns in this workspace","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["sales_pitch","survey","follow_up","lead_qualification","appointment","event_rsvp","briefing","custom"]},"agentId":{"type":"string","description":"The AI voice agent placing the calls"},"fromNumber":{"type":"string","description":"The number it calls from, E.164"},"voice":{"type":"string"},"schedule":{"type":"object","properties":{"startHour":{"type":"number","description":"Local hour calling may start, 0–23"},"endHour":{"type":"number","description":"Local hour calling stops"},"weekdays":{"type":"array","items":{"type":"number"},"description":"Days calling is permitted; 0 is Sunday"},"timezone":{"type":"string","description":"IANA zone the hours are read in, e.g. \"Europe/Riga\""},"maxAttempts":{"type":"number"},"retryBackoffMinutes":{"type":"array","items":{"type":"number"},"description":"Minutes to wait before attempt N+1"},"recurringHour":{"description":"For a briefing: the local hour it runs at","type":"number"}},"required":["startHour","endHour","weekdays","timezone","maxAttempts","retryBackoffMinutes"],"additionalProperties":false},"createdAt":{"type":"string"},"paused":{"type":"boolean"},"recurring":{"type":"boolean","description":"Briefings recur; a one-shot campaign does not"},"isTemplate":{"description":"Templates are reusable setups that are never dialled","type":"boolean"},"starred":{"type":"boolean"},"stats":{"type":"object","properties":{"total":{"type":"number"},"pending":{"type":"number"},"completed":{"type":"number"},"failed":{"type":"number"},"abandoned":{"type":"number"},"cancelled":{"type":"number"}},"required":["total","pending","completed","failed","abandoned","cancelled"],"additionalProperties":false,"description":"How the queue is going: one count per contact status"}},"required":["id","name","kind","agentId","fromNumber","voice","schedule","createdAt","paused","recurring","stats"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install has no campaign store, so there are no campaigns to act on","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"campaigns.view"}},"/api/v1/campaigns/{id}":{"get":{"operationId":"campaigns.get","summary":"One campaign by id","description":"What the campaign is and how its queue is going. The contacts, the prompt and the tools are not here; a member of the workspace reads those from campaigns.details.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"One campaign by id","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["sales_pitch","survey","follow_up","lead_qualification","appointment","event_rsvp","briefing","custom"]},"agentId":{"type":"string","description":"The AI voice agent placing the calls"},"fromNumber":{"type":"string","description":"The number it calls from, E.164"},"voice":{"type":"string"},"schedule":{"type":"object","properties":{"startHour":{"type":"number","description":"Local hour calling may start, 0–23"},"endHour":{"type":"number","description":"Local hour calling stops"},"weekdays":{"type":"array","items":{"type":"number"},"description":"Days calling is permitted; 0 is Sunday"},"timezone":{"type":"string","description":"IANA zone the hours are read in, e.g. \"Europe/Riga\""},"maxAttempts":{"type":"number"},"retryBackoffMinutes":{"type":"array","items":{"type":"number"},"description":"Minutes to wait before attempt N+1"},"recurringHour":{"description":"For a briefing: the local hour it runs at","type":"number"}},"required":["startHour","endHour","weekdays","timezone","maxAttempts","retryBackoffMinutes"],"additionalProperties":false},"createdAt":{"type":"string"},"paused":{"type":"boolean"},"recurring":{"type":"boolean","description":"Briefings recur; a one-shot campaign does not"},"isTemplate":{"description":"Templates are reusable setups that are never dialled","type":"boolean"},"starred":{"type":"boolean"},"stats":{"type":"object","properties":{"total":{"type":"number"},"pending":{"type":"number"},"completed":{"type":"number"},"failed":{"type":"number"},"abandoned":{"type":"number"},"cancelled":{"type":"number"}},"required":["total","pending","completed","failed","abandoned","cancelled"],"additionalProperties":false,"description":"How the queue is going: one count per contact status"}},"required":["id","name","kind","agentId","fromNumber","voice","schedule","createdAt","paused","recurring","stats"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install has no campaign store, so there are no campaigns to act on","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"campaigns.view"}},"/api/v1/agents/{id}/custom-tools":{"get":{"operationId":"agentTools.list","summary":"The tools of one agent that the workspace defined itself","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The tools of one agent that the workspace defined itself","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"parameters":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"http":{"anyOf":[{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"https://… with {argument} and {caller_phone} placeholders"},"method":{"default":"GET","type":"string","enum":["GET","POST"]},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"body":{"description":"For POST, with the same placeholders","type":"string","maxLength":10000},"timeoutMs":{"type":"integer","minimum":500,"maximum":8000}},"required":["url","method"],"additionalProperties":false},{"type":"null"}]},"notifyStaff":{"anyOf":[{"type":"boolean","const":true},{"type":"string","minLength":1}],"description":"For a tool without `http`: true sends every call that used it to the agent's staff emails and Telegram chat when the call is over; the name of one of its BOOLEAN arguments sends only the calls where the agent set it to true"}},"required":["name","description","parameters","http"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No agent with that id in this workspace, or no tool with that name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"agents.tools"}},"/api/v1/agents/{id}/custom-tools/{name}":{"put":{"operationId":"agentTools.put","summary":"Add a tool to an agent, or replace the one with this name","description":"Name it the way the prompt refers to it (letters, digits, underscores). `parameters` is the argument schema the model fills, with uppercase types as in the Gemini API. Header values are kept from the stored tool when sent as the mask the list shows. The agent uses the tool from its next call; mention it in the prompt so the agent knows when.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","minLength":1,"maxLength":1000},"parameters":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"http":{"anyOf":[{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"https://… with {argument} and {caller_phone} placeholders"},"method":{"default":"GET","type":"string","enum":["GET","POST"]},"headers":{"description":"Values are never read back; send them again to change them","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"body":{"description":"For POST, with the same placeholders","type":"string","maxLength":10000},"timeoutMs":{"type":"integer","minimum":500,"maximum":8000}},"required":["url"]},{"type":"null"}]},"notifyStaff":{"anyOf":[{"type":"boolean","const":true},{"type":"string","minLength":1}],"description":"For a tool without `http`: true sends every call that used it to the agent's staff emails and Telegram chat when the call is over; the name of one of its BOOLEAN arguments sends only the calls where the agent set it to true"}},"required":["description"]}}}},"responses":{"200":{"description":"Add a tool to an agent, or replace the one with this name","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"parameters":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"http":{"anyOf":[{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"https://… with {argument} and {caller_phone} placeholders"},"method":{"default":"GET","type":"string","enum":["GET","POST"]},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"body":{"description":"For POST, with the same placeholders","type":"string","maxLength":10000},"timeoutMs":{"type":"integer","minimum":500,"maximum":8000}},"required":["url","method"],"additionalProperties":false},{"type":"null"}]},"notifyStaff":{"anyOf":[{"type":"boolean","const":true},{"type":"string","minLength":1}],"description":"For a tool without `http`: true sends every call that used it to the agent's staff emails and Telegram chat when the call is over; the name of one of its BOOLEAN arguments sends only the calls where the agent set it to true"}},"required":["name","description","parameters","http"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The name is not letters, digits and underscores, or it is a built-in tool; The URL moved to another host while a header was sent as the mask; details.fields names the headers to enter again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No agent with that id in this workspace, or no tool with that name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"agents.tools"},"delete":{"operationId":"agentTools.delete","summary":"Remove one of an agent’s own tools","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Remove one of an agent’s own tools","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"deleted":{"type":"boolean"}},"required":["name","deleted"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No agent with that id in this workspace, or no tool with that name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"agents.tools"}},"/api/v1/agents/{id}/custom-tools/{name}/test":{"post":{"operationId":"agentTools.test","summary":"Call a tool’s endpoint once, as the agent would mid-call","description":"Only for tools with `http`. {caller_phone} is filled with a dummy number. Private and local addresses are refused.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"args":{"default":{},"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}}}}},"responses":{"200":{"description":"Call a tool’s endpoint once, as the agent would mid-call","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"result":{}},"required":["ok","result"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; The tool calls no endpoint; it only records its arguments","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace; No agent with that id in this workspace, or no tool with that name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"agents.tools"}},"/api/v1/calls/place":{"post":{"operationId":"calls.place","summary":"Ring a number with one of the workspace agents and fill its prompt for this call","description":"Read agents.get first: its `placeholders` are the keys `variables` should carry. Answers at once with a placementId; then call calls.wait with it. `background` is free text the agent is told about the person; `name` fills {callee_name}. Unfilled placeholders read \"unknown\" and are listed back. Send a `clientId` (or an Idempotency-Key header over REST) and a repeat of the same request within a day answers the earlier placement instead of ringing the person again; the same id with another number, agent or variables is refused (409 idempotency_conflict).","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"toNumber":{"type":"string","description":"The number to ring, E.164"},"agentId":{"description":"Required when the workspace has more than one enabled agent","type":"string"},"variables":{"description":"Values for the {placeholders} in the agent prompt, e.g. {\"company_name\": \"Acme\"}","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"name":{"description":"Who is being called; fills {callee_name}","type":"string"},"background":{"description":"Anything else the agent should know about this call, in prose","type":"string"},"language":{"description":"Language to open the call in, e.g. \"Lithuanian\"","type":"string"},"clientId":{"description":"Your own id for this request; a repeat with the same id within a day answers the earlier placement rather than placing a second call","type":"string","minLength":1,"maxLength":200}},"required":["toNumber"]}}}},"responses":{"202":{"description":"Ring a number with one of the workspace agents and fill its prompt for this call","content":{"application/json":{"schema":{"type":"object","properties":{"placementId":{"type":"string","description":"Pass this to calls.wait"},"status":{"type":"string","enum":["queued","calling"]},"agentId":{"type":"string"},"toNumber":{"type":"string"},"unfilled":{"type":"array","items":{"type":"string"},"description":"Placeholders in the agent prompt that variables did not fill; the agent reads them as \"unknown\""}},"required":["placementId","status","agentId","toNumber","unfilled"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own; The number is not 6–15 digits, optionally with a leading +; The workspace has several enabled agents and none was named; details.agents lists them; The chosen agent has no phone number to call from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace has used its plan up; details.reasons says which limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation; The workspace has outbound calls turned off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No enabled agent with that id in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The clientId or Idempotency-Key was used within the day for a call with another toNumber, agentId or variables","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The workspace checks numbers before calling and a minute to this one costs more than its limit, or cannot be priced; details carry eurPerMinute and maxEurPerMinute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The carrier did not accept the call, or the dial failed; details.lastError says why, and details.placementId is the placement it was queued as","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install has no campaign store, so nothing can be placed; This install has no telephony configured, so nothing can be dialled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"campaigns.run"}},"/api/v1/calls/wait":{"post":{"operationId":"calls.wait","summary":"Wait for a placed call to finish and read its outcome","description":"Blocks up to timeoutSeconds (default 30, max 120) until the call is finished and analysed, or the person could not be reached, then answers with the state and, once there is one, the call with its outcome and summary. MCP clients time out at 60 s by default: pass 45 or less and call again. Send timeoutSeconds 0 for a plain status read. Call again while status is not done, not_reached or failed.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"placementId":{"type":"string","description":"From calls.place"},"timeoutSeconds":{"default":30,"type":"integer","minimum":0,"maximum":120}},"required":["placementId"]}}}},"responses":{"200":{"description":"Wait for a placed call to finish and read its outcome","content":{"application/json":{"schema":{"type":"object","properties":{"placementId":{"type":"string"},"status":{"type":"string","enum":["queued","ringing","in_call","analysing","done","not_reached","failed"]},"call":{"description":"The call, once there is one; complete with outcome and summary at \"done\"","type":"object","properties":{"id":{"type":"string"},"agentId":{"type":"string","description":"The AI voice agent that handled the call"},"direction":{"type":"string","enum":["inbound","outbound"]},"from":{"type":"string","description":"Caller number, E.164 where known"},"to":{"type":"string","description":"Called number"},"status":{"type":"string","enum":["in_progress","completed_action","incomplete_call","transferred","failed"]},"startedAt":{"type":"string"},"durationSeconds":{"type":"number"},"summary":{"description":"AI summary in the workspace language","type":"string"},"sentiment":{"type":"string"},"score":{"description":"Overall quality score, 0–100","type":"number"},"keyFacts":{"type":"array","items":{"type":"string"}},"nextSteps":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"date":{"type":"string"},"priority":{"type":"string"}},"required":["task","date","priority"],"additionalProperties":false}},"clientName":{"type":"string"},"companyName":{"type":"string"},"needsCallback":{"type":"boolean"},"transferredTo":{"description":"Set when the call was handed to a human","type":"string"},"handoffs":{"description":"Each time the call was passed to another assistant of the workspace, oldest first","type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"toAgentId":{"type":"string"},"toName":{"type":"string"},"reason":{"type":"string"},"summary":{"type":"string"}},"required":["at","toAgentId","toName"],"additionalProperties":false}},"analysedAt":{"type":"string"},"outcome":{"description":"What the agent's outcome tool recorded, as the agent passed it","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"outcomeTool":{"description":"The tool that recorded `outcome`, e.g. log_call","type":"string"},"outcomeSource":{"description":"agent: the agent called its outcome tool on the call; transcript: filled from the transcript afterwards because it did not","type":"string","enum":["agent","transcript"]},"campaignId":{"description":"The campaign an outbound call belongs to","type":"string"},"placementId":{"description":"For a call placed with calls.place: the id calls.wait takes","type":"string"}},"required":["id","agentId","direction","from","to","status","startedAt","durationSeconds","needsCallback"],"additionalProperties":false},"lastError":{"description":"Why the person was not reached, or why the dial failed","type":"string"}},"required":["placementId","status"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No placement with that id in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install has no campaign store, so nothing can be placed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.view"}},"/api/v1/calls/callback":{"post":{"operationId":"calls.callback","summary":"Ring back somebody who has called this workspace before","description":"For an assistant that promised a callback: only a number with an earlier inbound call can be rung, and the agent that took that call is used unless another is named. `reason` tells the agent why it is calling back; the last call’s summary is added for it. Then call calls.wait with the placementId.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"toNumber":{"type":"string","description":"The number that called, E.164"},"reason":{"description":"Why the business is calling back, in prose","type":"string","maxLength":1000},"agentId":{"description":"Defaults to the agent that took their last call","type":"string"},"language":{"type":"string"}},"required":["toNumber"]}}}},"responses":{"202":{"description":"Ring back somebody who has called this workspace before","content":{"application/json":{"schema":{"type":"object","properties":{"placementId":{"type":"string","description":"Pass this to calls.wait"},"status":{"type":"string","enum":["queued","calling"]},"agentId":{"type":"string"},"toNumber":{"type":"string"},"unfilled":{"type":"array","items":{"type":"string"},"description":"Placeholders in the agent prompt that variables did not fill; the agent reads them as \"unknown\""}},"required":["placementId","status","agentId","toNumber","unfilled"],"additionalProperties":false}}}},"400":{"description":"Input failed validation; details.issues names each field; Called without x-tenant-id by a principal that has no workspace of its own; The number is not 6–15 digits, optionally with a leading +; The chosen agent has no phone number to call from","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credentials, or an unknown or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace has used its plan up; details.reasons says which limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The credentials do not permit this operation; The workspace has outbound calls turned off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nobody from this number has called the workspace; use calls.place with the place_calls scope instead; No enabled agent with that id in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The workspace checks numbers before calling and a minute to this one costs more than its limit, or cannot be priced; details carry eurPerMinute and maxEurPerMinute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The carrier did not accept the call; details.lastError says why","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"This install has no campaign store, so nothing can be placed; This install has no telephony configured, so nothing can be dialled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearerKey":[]},{"apiKeyHeader":[]}],"x-exposure":"public","x-permission":"calls.callback"}},"/api/v1/auth/{provider}":{"get":{"operationId":"auth.oauthStart","summary":"Begin sign-in with Google or Microsoft","description":"Redirects the browser to the provider; the provider returns to /auth/{provider}/callback.","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["google","microsoft"]}}],"responses":{"302":{"description":"Redirects to the provider"},"400":{"description":"Input failed validation; details.issues names each field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Nothing at that id within this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; wait Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"That provider has no client id and secret on this install","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[],"x-exposure":"public","x-raw":true}}},"components":{"securitySchemes":{"bearerKey":{"type":"http","scheme":"bearer","description":"Authorization: Bearer zv_..."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key"}},"schemas":{"Error":{"type":"object","required":["error","hint"],"properties":{"error":{"type":"string","description":"Stable snake_case code"},"hint":{"type":"string","description":"What went wrong and what to do about it"},"details":{"description":"Optional structured detail, e.g. validation issues"}}}}}}