{"openapi":"3.0.3","info":{"title":"Elvo Public API","version":"1.4.0","description":"Welcome to the Elvo REST API — the programmatic way to work with conversations, comments, contacts, and the workspace itself: members and invitations, teams, channels, tags, canned responses, automation rules, the AI knowledge base, performance reports, and your own out-of-office status. Elvo is AI-first: everything an admin can do in the dashboard is meant to be doable from here, so an agent or a pipeline can run the workspace without opening the UI.\n\n## Getting started\n\n### 1. Create an API token\n\nTokens are created inside Elvo under **Settings → API & agents** (Icelandic: **API og gervigreind**).\n\n➡️ **[Open the token settings](/?settings=open&tab=api)** (must be signed in as an admin)\n\nBuilding an AI agent? Read [llms.txt](https://elvo.is/llms.txt) first: it starts with a short agent quickstart. The human guide is [Use your own AI agent with Elvo](https://elvo.is/help/ai-agents).\n\nYou'll be prompted for a **name**, a **scope**, and an optional **expiry**:\n\n- **Read** (recommended default) — token can only make `GET` and `HEAD` requests.\n- **Write** — token can also `POST`, `PATCH`, and `DELETE` to create and update resources.\n\nThe plaintext token is shown **once**. Copy it immediately and store it in your secrets manager — Elvo never displays it again.\n\n### 2. Authenticate\n\nSend the token in the `Authorization` header on every request:\n\n```\nAuthorization: Bearer elvo_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n```\n\n### 3. Make a request\n\nBase URL: `https://elvo.is/api/public/v1`\n\n```bash\ncurl https://elvo.is/api/public/v1/conversations \\\n  -H \"Authorization: Bearer elvo_...\"\n```\n\n## Conventions\n\n- **Envelope**: single resources return `{ \"data\": ... }`, lists return `{ \"data\": [...], \"pagination\": { \"next_cursor\", \"has_more\" } }`.\n- **Pagination**: cursor-based. Pass `?cursor=<value>&limit=25` (max 100).\n- **IDs**: every id (conversation, comment, contact, user, team, tag) is a UUID. `conversation_number` is the short number people see in the dashboard, not an id.\n- **Sort order**: list endpoints return newest first (`created_at` descending) unless the endpoint says otherwise.\n- **Timestamps**: ISO 8601 in UTC.\n- **Field casing**: `snake_case` everywhere — both query parameters and JSON fields.\n- **Compatibility**: new response fields and new enum values may appear in any release without a version bump — ignore fields you don't know. Removing or renaming a field, or changing its type, only happens in a new API version.\n- **Errors**: `{ \"error\": { \"type\", \"message\", \"details\" } }` with types `authentication_error`, `authorization_error`, `validation_error`, `not_found`, `method_not_allowed` (HTTP 405, with an `Allow` header), `rate_limit_exceeded`, `conflict`, `plan_limit` (HTTP 402 — a plan cap on seats, teams, rules or canned responses; `details` carries `{ feature, limit, current, plan }`), `internal_error`.\n- **Permissions**: a token acts as the member who created it. Routes the dashboard reserves for organization admins (members, invitations, teams, knowledge) require a token created by an admin — otherwise `403 authorization_error`. If that member is demoted or deactivated, their tokens follow within a minute.\n- **Unknown query parameters**: send only the parameters an operation documents. Unknown parameters are rejected with `400 validation_error`, so a typo does not silently return unfiltered data.\n- **Rate limits**: 200 reads/minute and 60 writes/minute per token (sliding window; `GET` routes count as reads, every other method as writes). Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (an ISO 8601 timestamp, not unix seconds). A `429` also carries `Retry-After` (seconds) and `error.details.retry_after_seconds`. Error responses other than 429 may omit the `X-RateLimit-*` headers.\n\n## Conversation statuses\n\n| Status | Meaning |\n|---|---|\n| `open` | Needs a reply or action from your team. The working queue. |\n| `pending` | Your team replied and is waiting for the customer. |\n| `resolved` | Done. There is no `closed` status: to close a conversation, set `resolved`. |\n| `ignored` | Spam or noise (the dashboard's spam folder). |\n| `deleted` | In the trash. |\n\nA new customer message on a `pending`, `resolved`, `ignored` or `deleted` conversation normally moves it back to `open`. Sending a reply on a `resolved`, `ignored` or `deleted` conversation reopens it unless you pass a `status`.\n\nTwo flags sit beside the status and do not change it:\n\n- **Archived** (`archived_at` set): filed out of every inbox view. `GET /conversations` includes archived conversations by default (`archived=all`); pass `archived=hide` to match what the inbox shows.\n- **Snoozed** (`snooze_until` in the future): hidden until that time. `GET /conversations` hides snoozed conversations by default (`snoozed=hide`); pass `snoozed=all` or `snoozed=only` to see them.\n\n## Recipes\n\n**Triage a backlog**\n1. `GET /conversations?status=open&assigned_to_id=<bot-user-id>`\n2. For each: read latest comments via `GET /conversations/{id}/comments?include_internal=true`\n3. Reply or close: `POST /conversations/{id}/comments` with `{ \"content\": \"...\", \"status\": \"resolved\" }`\n\n**Reply with internal note**\n```bash\ncurl -X POST https://elvo.is/api/public/v1/conversations/<id>/comments \\\n  -H \"Authorization: Bearer elvo_...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"content\":\"Asked accounting to confirm refund eligibility.\",\"is_internal\":true}'\n```\n\n**Label and reassign**\n```bash\ncurl -X PATCH https://elvo.is/api/public/v1/conversations/<id> \\\n  -H \"Authorization: Bearer elvo_...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"tags\":[\"billing\",\"vip\"],\"assigned_to_id\":\"<user-uuid>\"}'\n```\n\n## Managing the workspace\n\nThe same admin surface as **Settings** in the dashboard, so provisioning and configuration can live in your own systems.\n\n**Onboard a new agent** (admin-owned write token)\n1. `POST /invitations` with `{ \"email\", \"name\", \"role\": \"agent\", \"team_ids\": [\"<team-uuid>\"] }` — Elvo emails the invitation; the member appears in `GET /users` once they accept, already in those teams.\n2. Later: `POST /teams/{id}/members` / `DELETE /teams/{id}/members/{userId}` to move people, `PATCH /users/{id}` to change role, `DELETE /users/{id}` to offboard (deactivates; `POST /users/{id}/reactivate` undoes it).\n\n**Keep routing rules in version control**\n`GET /automation-rules` returns every rule as the exact JSON the engine runs (conditions + actions). Commit that to your repo; on change, `PATCH /automation-rules/{id}` (or `POST` a new rule) from CI. Rule bodies keep the engine's own camelCase keys inside `conditions`/`actions` so an export re-applies verbatim; tag actions carry tag *names*. Who changed which rule and when is recorded in the workspace activity log.\n\n**Keep the AI knowledge fresh from a pipeline**\nCreate one `POST /knowledge-sources` (`kind: \"text\"`) per document in your source of truth and remember the returned `id`. When the document changes, `PATCH /knowledge-sources/{id}` with the new `content` — Elvo re-chunks and re-embeds asynchronously (`status` goes `processing` → `ready`); a PATCH whose title and content match what is stored is a no-op (no re-embedding, no status change), so a pipeline can PATCH on every run. URL sources can be re-fetched with `POST /knowledge-sources/{id}/sync`. Documents (pdf, docx, …) go in as `file` sources via `POST /knowledge-sources/files`, and `PUT /knowledge-sources/{id}/file` swaps the file behind one while keeping its id — the nightly \"re-upload the current price list\" job.\n\n**Pull reporting**\n`GET /reports/performance?period=last_30_days` returns the Frammistaða dashboard as JSON — first-reply and resolution times, volumes, AI share, per-agent and per-team stats — with the previous period for comparison.\n\n## Webhooks\n\nRegister a webhook endpoint and Elvo POSTs you a signed JSON payload whenever a subscribed event happens — no polling.\n\n**Events**\n\n- `conversation.created` — a new conversation landed (any channel, including ones you create via this API). Two gaps to know about: conversations an agent composes outbound from the dashboard start as silent drafts and never emit this event (you'll see their first `comment.created` instead — treat unknown conversation ids by fetching `GET /conversations/{id}`), and historical mailbox imports never emit webhooks at all.\n- `conversation.updated` — status, assignee, team, subject, tags, contact, or snooze state changed. Deletion arrives as `conversation.updated` with `data.conversation.status: \"deleted\"` — there is no separate deleted event, and a conversation that is purged entirely (hard delete) may produce no event.\n- `comment.created` — a new customer message or agent reply (check `data.comment.type`). **Internal notes are never delivered.** Comments your own integration creates via `POST /conversations/{id}/comments` also fire this event back at you — record the comment `id` from your POST responses and skip matching deliveries to avoid loops.\n\n**Register an endpoint** (requires a write-scope token):\n\n```bash\ncurl -X POST https://elvo.is/api/public/v1/webhooks \\\n  -H \"Authorization: Bearer elvo_...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"url\":\"https://example.com/elvo-webhook\",\"events\":[\"conversation.created\",\"comment.created\"]}'\n```\n\nThe response includes a `secret` (`whsec_...`) — shown **once**, store it safely.\n\n**Verify signatures.** Every delivery carries three headers: `Elvo-Webhook-Id`, `Elvo-Webhook-Timestamp` (unix seconds), and `Elvo-Webhook-Signature` (`v1=<hex>`). The signature is HMAC-SHA256 over `<id>.<timestamp>.<raw body>` using your endpoint secret:\n\n```js\nimport { createHmac, timingSafeEqual } from 'node:crypto'\n\nfunction verify(req, rawBody, secret) {\n  const id = req.headers['elvo-webhook-id']\n  const ts = req.headers['elvo-webhook-timestamp']\n  const expected = 'v1=' + createHmac('sha256', secret).update(`${id}.${ts}.${rawBody}`).digest('hex')\n  const given = req.headers['elvo-webhook-signature'] ?? ''\n  return given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected))\n}\n```\n\nUse the **entire secret string as the HMAC key, including the `whsec_` prefix** — do not strip or base64-decode it. Reject deliveries whose timestamp is older than a few minutes to prevent replay.\n\n**Delivery semantics**\n\n- At-least-once: respond with any 2xx within 10 seconds. Anything else is retried for **3 total attempts within about a minute** (immediately, then ~20s and ~40s later); dedupe on the payload `id`, which is stable across retries.\n- Redirects are **not** followed — a 3xx response counts as a failed delivery. Register the final URL.\n- A burst of changes can produce several `conversation.updated` events for one conversation. Payloads carry the conversation's state *at delivery time*, so order by `data.conversation.updated_at` (not the event's `created_at`) when reconciling.\n- An endpoint is automatically disabled (`active: false` with a `disabled_reason`) after 25 consecutive failed delivery attempts. Re-enable with `PATCH /webhooks/{id} {\"active\": true}` after fixing your receiver — this resets the failure counter (changing `url` alone does not). Events that occur while an endpoint is disabled are **not** queued or replayed; after re-enabling, reconcile by listing conversations.\n- To rotate a signing secret: register a second endpoint (same host, different path), verify against both secrets during cutover, then delete the old endpoint.\n- Destination URLs must be public https addresses; one endpoint per URL per organization.\n\n## Custom integrations — your data in AI answers\n\nThe REST API above is one half of the integration story. The other half runs in the opposite direction: Elvo pulls live context out of **your** systems and puts it in front of agents and the AI. Built-in connectors exist for DK, Business Central, Payday, Regla, Shopify, WooCommerce, HubSpot, Dropp, and Abler — and a **custom integration** covers everything else with a single HTTPS endpoint that you host.\n\nWhenever an agent opens a conversation or the AI drafts a reply, Elvo POSTs the contact's identity and their latest message to your endpoint. You look the customer up — by email, or by parsing an order/member/shipment id out of the message — and respond with structured JSON: `fields` render in the agent's sidebar, `aiSummary` (or `markdown`) is what the AI reply draft sees.\n\n**Request** (Elvo → your endpoint):\n\n```\nPOST <your endpoint URL>\nAuthorization: Bearer <token generated by Elvo>\nContent-Type: application/json\nX-Elvo-Organization: <organization id>\n\n{\n  \"email\": \"customer@example.com\",\n  \"name\": \"Customer Name\",\n  \"organizationId\": \"uuid\",\n  \"ticketId\": \"uuid\" | null,\n  \"ticketSubject\": \"Question about order 12345\" | null,\n  \"ticketBodyExcerpt\": \"first ~2 KB plain text of the latest customer message\" | null,\n  \"customerMessage\": \"the FULL plain text of the latest customer message\" | null\n}\n```\n\n**Response** — `200` with data, or `404` when there's no match (any other status or a timeout is logged and Elvo proceeds without your data):\n\n```json\n{\n  \"title\": \"Order #12345\",\n  \"subtitle\": \"Acme ehf.\",\n  \"fields\": [\n    { \"label\": \"Status\", \"value\": \"Shipped\", \"link\": \"https://internal.example.com/orders/12345\" },\n    { \"label\": \"ETA\", \"value\": \"2026-05-09\" }\n  ],\n  \"aiSummary\": \"Order 12345 shipped 2026-05-02; ETA 2026-05-09; customer is on the corporate plan.\"\n}\n```\n\n**Behavior contract**: 5-second timeout; successful responses cached ~5 minutes per (integration, contact email, customer message); responses capped at 256 KB; HTTPS only, no private/internal hostnames; reject any request whose bearer token doesn't match the one Elvo generated for you.\n\nSet it up under **Settings → Integrations → Custom integration** — Elvo generates a complete setup brief there (this contract plus sample Node and Python servers) that you can hand directly to the engineer building the endpoint.\n\n## Embedding the chat widget (Spjallþjónn)\n\nThe customer-facing conversation surface is the Elvo chat widget — an AI agent that answers visitors from your knowledge and hands complex cases to your team. Embed it on any site by pasting the snippet shown in **Settings** when you set up the Spjallþjónn:\n\n```html\n<script type=\"text/javascript\">\n!function(w,d,n){function l(){var s=d.createElement(\"script\");\n  s.type=\"text/javascript\";s.async=!0;s.src=\"https://beacon.elvo.is/beacon/loader.js\";\n  d.head.appendChild(s)}\n  w.Beacon=n=function(){(w.Beacon.q=w.Beacon.q||[]).push(arguments)};\n  if(d.readyState===\"complete\") l(); else w.addEventListener(\"load\", l, false);\n}(window,document,window.Beacon||function(){});\n</script>\n<script type=\"text/javascript\">Beacon('init', { beacon: 'YOUR_PUBLIC_KEY' });</script>\n```\n\nWidget sessions are anonymous; when a conversation needs follow-up, the AI asks the visitor for their name and email and links the conversation to a contact. Widget conversations land in your workspace like any other channel — you can read them through this API and receive them via webhooks.\n\n**A note on scope.** API tokens act as the team member who created them: this is an agent-scoped API, built for automations and systems working *alongside* your team. There is no end-user-scoped token today — an end user of your product can't call this API to hold their own conversation. For customer-facing experiences, embed the chat widget. If your use case needs programmatic end-user conversations (a native app, an embedded portal), email **elvo@elvo.is** — we want to hear about it.\n\n## Revoking a token\n\nAny admin can revoke a token at any time from the same settings page. Revocation takes effect immediately — the next request returns 401.\n\n## Support\n\nQuestions or missing endpoints? Email **elvo@elvo.is**.\n"},"servers":[{"url":"https://elvo.is/api/public/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Conversations","description":"Customer conversations (also called tickets). Read, update status/tags/assignee/team, snooze. Status values: `open`, `pending`, `resolved`, `ignored`, `deleted` (there is no `closed`; see **Conversation statuses** above)."},{"name":"Comments","description":"Messages and internal notes attached to a conversation. Send replies and add notes."},{"name":"Chats","description":"Internal team chats (DMs, group chats, team/org channels). Threads with teammates — distinct from customer conversations. See the two-axis model below."},{"name":"Contacts","description":"People who have written in. Address book entries and ad-hoc senders."},{"name":"Mentions","description":"Where the token's user was @-mentioned, across conversations and chats. The bell, as an API."},{"name":"Organization","description":"The workspace owning the API token."},{"name":"Attachments","description":"Upload files to include in replies via `attachment_ids`."},{"name":"Webhooks","description":"Outbound webhook endpoints — get notified of events instead of polling."},{"name":"Users","description":"Team members in the organization. Members join through invitations; leaving is a soft deactivation that keeps their history and can be undone. Role changes, deactivation and reactivation need an admin-owned token."},{"name":"Invitations","description":"Pending invitations to join the organization. Creating one sends the invitee an email with a link; on acceptance they become a User with the given role and team memberships. Writing needs an admin-owned token."},{"name":"Teams","description":"Teams group conversations, channels and members into separate inboxes. Every organization has one default team (channels without an explicit team route there). Reads are open to any member; creating, editing, deleting and managing members require an admin-owned token."},{"name":"Channels","description":"The inbound channels an organization receives conversations on: forwarding addresses and synced mailboxes, Meta pages (Messenger, Instagram), WhatsApp numbers, the website widget and the phone line. Read-only for now — connecting a channel involves an OAuth consent or a DNS/forwarding step that has to happen in the dashboard (Settings → Tölvupóstur / Samfélagsmiðlar / Spjallþjónn). Every channel routes to exactly one team; `team_id` is where its conversations land."},{"name":"Tags","description":"The organization's tag vocabulary (\"merki\"). Tags are first-class rows with an id, a name that is unique per organization case-insensitively, an optional palette color and emoji, and a usage count. Conversations reference tags by NAME (`tags` on a conversation, the `tag` list filter); this resource manages the vocabulary itself. Any member's token may read and write tags, as in the dashboard."},{"name":"Canned responses","description":"Saved reply templates (\"stöðluð svör\") agents insert from the composer. A response is available to everyone (empty `team_ids`) or restricted to one or more teams. `exclude_from_ai: true` keeps a response out of AI reply drafts while agents can still insert it. Any member's token may read and write them, as in the dashboard; the list applies the same visibility rule the composer does for the token's owner."},{"name":"Automation rules","description":"Routing rules the engine evaluates against every new inbound conversation, in `priority` order (lower first), applying each matching rule's actions until one says `stop_processing_more_rules`.\n\nAny member's token can read and write rules (the dashboard grants the same). The rule JSON — `conditions` and `actions` — is the engine's own format, returned verbatim so you can keep rules in version control: `GET` the list, commit it, and `PATCH` a rule with the same objects to re-apply. **Inside those two arrays the keys are camelCase** (`toEmail`, `includeAttachments`, `userIds`, `teamId`, `roundRobin`, `onlyOnline`) — the one deliberate exception to this API's snake_case, kept so exported rules round-trip byte-for-byte. Everything at the top level of a rule (`is_active`, `condition_logic`, `created_at`, …) is snake_case as usual.\n\n`add_tag` / `remove_tag` carry tag **names**, not ids, like `tags` everywhere else on this API. Unknown names are created on first use.\n\nThere is no server-side rule history: Elvo's activity log records who changed which rule and when, and the full rule JSON is what you keep in git."},{"name":"Knowledge","description":"The AI knowledge base (Þekking in the dashboard): the documents the AI agents answer from. Each source is chunked and embedded asynchronously — a new or changed source is `status: processing` until the job finishes, then `ready` (with `chunk_count`) or `error` (with `error_message`).\n\n**Keeping the knowledge fresh from your own system.** Create one text source per document you own (`POST` with `kind: \"text\"`), remember its `id`, and `PATCH` its `content` whenever the document changes. A PATCH whose `title`/`content` match what is stored is a no-op (200, no re-embedding, `status` unchanged), so a pipeline can safely PATCH on every run. URL and Google Docs/Sheets sources are refreshed by Elvo on a schedule; `POST /knowledge-sources/{id}/sync` forces a refresh, and unchanged extracted text skips re-embedding by content hash.\n\n**Files.** `POST /knowledge-sources/files` uploads a document (pdf, txt, md, docx, xlsx, xls, csv; up to 4 MB) as a `file` source, and `PUT /knowledge-sources/{id}/file` swaps the document behind an existing file source while keeping its id and title — so a nightly job can re-upload the current price list, or an agent can replace an outdated PDF. A replacement whose extracted text is unchanged skips re-embedding. Files over 4 MB are dashboard-only (the platform caps API request bodies at ~4.5 MB).\n\nReads are open to any token; creating, updating, deleting and syncing require an admin-owned token."},{"name":"Reports","description":"Read-only reporting. `GET /reports/performance` returns exactly the data behind the dashboard's **Frammistaða** (performance) tab — the same metric definitions, the same 5-minute cache — so a number in the API always matches the number on screen. The Frammistaða feature is included on **Vöxtur (Pro)** and above. Access follows the dashboard rule: organization admins always; other members only when the organization has opened the tab to all members (Settings → Frammistaða). `period` and `comparison` values use the API's own snake_case spellings (`last_30_days`, `previous_period`, …), not the dashboard's internal ones."},{"name":"Me","description":"The token owner — the member who created the token. Out of office (Fjarvera) is set here: a purple presence dot and a banner for teammates in internal chats, and one auto-reply per sender from the member's personal connected mailboxes. It is user-level, so one status covers every organization the member belongs to; other members see it as `out_of_office` on the User resource."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Elvo API Token"}},"schemas":{"Conversation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"conversation_number":{"nullable":true,"type":"number"},"subject":{"type":"string"},"status":{"type":"string","enum":["open","pending","resolved","ignored","deleted"]},"contact_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"assigned_to_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"team_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channel":{"nullable":true,"description":"Where the conversation came in. Inbound channels use the `Channel.type` vocabulary (`email.postmark`, `email.gmail`, `email.microsoft`, `messenger`, `instagram`, `whatsapp`, `beacon_chat`, `beacon_form`, `voice`); conversations created through this API carry the `channel` given at creation (`webhook`, `manual`, `phone`). Null for legacy conversations without a recorded channel.","type":"string"},"channel_id":{"nullable":true,"description":"The `Channel.id` the conversation arrived on (the forwarding address, mailbox connection UUID, page id, phone-number id or widget UUID) — pair with `channel` to look it up in `GET /channels`. For widget conversations this is the beacon UUID. Null for voice calls, API-created conversations and legacy rows.","type":"string"},"tags":{"nullable":true,"description":"Tag names on the conversation, in the order they were added. Tags are an org-wide vocabulary (each also carries a color/emoji, managed in the dashboard); names are case-sensitive. Null when the conversation has no tags.","type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"Last change: a new message, reply or internal note, or a change to status, assignee, team, tags, subject or snooze. Inbound email is stamped with its sent time, which can be slightly earlier than its arrival."},"resolved_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"snooze_until":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"snoozed_by_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"snooze_discard_on_reply":{"type":"boolean"},"snooze_leave_in_inbox":{"type":"boolean"},"archived_at":{"nullable":true,"description":"When the conversation was archived (Geymsla). An archived conversation keeps its status but is out of every inbox view: auto-replies, for example, are born archived. Null when not archived.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"related_conversation_ids":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"contact":{"description":"The conversation's contact (id, email, name), or null when it has none. Omitted from webhook payloads.","example":{"id":"9c2e4f1a-7b3d-4e5f-8a6b-1c2d3e4f5a6b","email":"jon@example.is","name":"Jón Jónsson"},"nullable":true,"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string"},"name":{"nullable":true,"type":"string"}},"required":["id","email","name"]},"last_message_at":{"description":"When the newest customer message or agent reply was written (internal notes and system entries excluded). Null when there are none. Omitted from webhook payloads.","example":"2026-09-28T14:03:11.000Z","nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"last_customer_message_at":{"description":"When the newest message from the customer arrived. Null when the customer has not written. Omitted from webhook payloads.","example":"2026-09-28T13:55:42.000Z","nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"message_count":{"description":"Number of customer messages plus agent replies (internal notes, system entries and deleted messages excluded). Omitted from webhook payloads.","example":4,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","conversation_number","subject","status","contact_id","assigned_to_id","team_id","channel","channel_id","tags","created_at","updated_at","resolved_at","snooze_until","snoozed_by_id","snooze_discard_on_reply","snooze_leave_in_inbox","archived_at","related_conversation_ids"]},"Comment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"conversation_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"type":{"type":"string","enum":["customer","agent","system"]},"user_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"contact_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"content":{"type":"string","description":"The stored body, verbatim. `content_format` says whether it is HTML or plain text. Agents that want text should read `content_text` instead."},"content_format":{"type":"string","enum":["html","text"],"description":"`html` for most email and composer replies. `text` for internal notes and chat-style channels, which are plain text with a small markdown subset (`**bold**`, `*italic*`, `` `code` ``, bare URLs, `@[Name]` mentions)."},"content_text":{"type":"string","description":"Plain-text rendering of `content`: `<style>`/`<script>` removed, `<br>` and block ends become newlines, entities decoded, links written as `label (url)`. On customer-visible messages the quoted reply history (for example \"On ... wrote:\" and the quoted email below it) is removed, so each message reads once. The full original stays in `content`."},"from":{"nullable":true,"description":"Email sender as recorded on the message, `Name <email>` or a bare address. Null when the message has no email record (internal notes, chat channels, many API-created messages).","type":"string"},"to":{"nullable":true,"description":"Email TO recipients as recorded on the message (same format as `from`). Null when not recorded.","type":"array","items":{"type":"string"}},"cc":{"nullable":true,"description":"Email CC recipients as recorded on the message (same format as `from`). Null when not recorded.","type":"array","items":{"type":"string"}},"is_internal":{"type":"boolean"},"quoted_comment_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"attachments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"file_name":{"type":"string"},"file_type":{"type":"string"},"file_size":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"url":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","file_name","file_type","file_size","url","created_at"]}},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","conversation_id","type","user_id","contact_id","content","content_format","content_text","from","to","cc","is_internal","quoted_comment_id","attachments","created_at","updated_at"]},"Draft":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"conversation_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"content":{"type":"string"},"to":{"nullable":true,"type":"array","items":{"type":"string"}},"cc":{"nullable":true,"type":"array","items":{"type":"string"}},"bcc":{"nullable":true,"type":"array","items":{"type":"string"}},"mode":{"nullable":true,"type":"string","enum":["reply","reply_all","forward","new"]},"subject":{"nullable":true,"type":"string"},"attachment_ids":{"nullable":true,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"author_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","conversation_id","content","to","cc","bcc","mode","subject","attachment_ids","author_id","created_at","updated_at"]},"Chat":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"type":{"type":"string","enum":["dm","group","team","org","personal"]},"subject":{"type":"string"},"emoji":{"nullable":true,"type":"string"},"participant_ids":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","type","subject","emoji","participant_ids","created_at","updated_at"]},"ChatMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"chat_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"user_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"content":{"type":"string"},"quoted_comment_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"attachments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"file_name":{"type":"string"},"file_type":{"type":"string"},"file_size":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"url":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","file_name","file_type","file_size","url","created_at"]}},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","chat_id","user_id","content","quoted_comment_id","attachments","created_at","updated_at"]},"Contact":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"nullable":true,"type":"string"},"phone":{"nullable":true,"type":"string"},"company":{"nullable":true,"type":"string"},"in_address_book":{"type":"boolean"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","email","name","phone","company","in_address_book","created_at","updated_at"]},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"nullable":true,"type":"string"},"role":{"type":"string","enum":["agent","admin"]},"deactivated_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"team_ids":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"out_of_office":{"nullable":true,"type":"object","properties":{"ends_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"message":{"type":"string"},"delegate_user_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["ends_at","message","delegate_user_id"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","email","name","role","deactivated_at","team_ids","out_of_office","created_at"]},"Organization":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"slug":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","name","slug","created_at"]},"Attachment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"file_name":{"type":"string"},"file_type":{"type":"string"},"file_size":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"url":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","file_name","file_type","file_size","url","created_at"]},"TimelineEvent":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"type":{"type":"string","description":"Event type, e.g. `ticket.status_changed`, `ticket.assigned`, `ticket.tagged`. The set of types can grow in any release."},"actor_type":{"type":"string","description":"`user`, `ai`, `system`, or `api`."},"actor_id":{"nullable":true,"description":"The acting user, or null for system/AI actors.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"actor_name":{"nullable":true,"type":"string"},"before":{"nullable":true,"description":"State before the change, when the event records one. Internal and unstable: this object is the dashboard's own event record, passed through as stored. Its keys are camelCase and may change or disappear in any release without notice. Show it or log it, but do not build logic on its shape. For the current state, read the conversation itself.","type":"object","additionalProperties":{}},"after":{"nullable":true,"description":"State after the change, when the event records one. Internal and unstable: this object is the dashboard's own event record, passed through as stored. Its keys are camelCase and may change or disappear in any release without notice. Show it or log it, but do not build logic on its shape. For the current state, read the conversation itself.","type":"object","additionalProperties":{}},"metadata":{"nullable":true,"description":"Event-specific context. Request IP / user agent are stripped. Internal and unstable: this object is the dashboard's own event record, passed through as stored. Its keys are camelCase and may change or disappear in any release without notice. Show it or log it, but do not build logic on its shape. For the current state, read the conversation itself.","type":"object","additionalProperties":{}},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","type","actor_type","actor_id","actor_name","before","after","metadata","created_at"],"description":"A timeline event. `id`, `type`, `actor_*` and `created_at` are the stable part of the contract. `before`, `after` and `metadata` are internal, unstable payloads (camelCase keys, shape not versioned)."},"Mention":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Notification id (stable cursor key)."},"comment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"conversation_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The conversation or chat the mention lives in."},"kind":{"type":"string","enum":["conversation","chat"],"description":"`conversation` = customer ticket (read it via /conversations), `chat` = internal chat (via /chats)."},"subject":{"type":"string"},"author_id":{"nullable":true,"description":"Who wrote the mentioning comment.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"content":{"type":"string","description":"The mentioning comment, plain text + note dialect."},"is_internal":{"type":"boolean"},"read_at":{"nullable":true,"description":"When the user cleared the bell notification, or null.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","comment_id","conversation_id","kind","subject","author_id","content","is_internal","read_at","created_at"]},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"url":{"type":"string"},"description":{"nullable":true,"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["conversation.created","conversation.updated","comment.created"]}},"active":{"type":"boolean"},"consecutive_failures":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"last_success_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"last_failure_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"last_failure_reason":{"nullable":true,"type":"string"},"disabled_reason":{"nullable":true,"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","url","description","events","active","consecutive_failures","last_success_at","last_failure_at","last_failure_reason","disabled_reason","created_at"]},"WebhookEndpointWithSecret":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"HMAC signing secret (`whsec_...`). Returned only in this creation response — store it immediately."}}}]},"WebhookEventPayload":{"type":"object","required":["id","type","created_at","data"],"description":"The JSON body Elvo POSTs to your webhook endpoint.","properties":{"id":{"type":"string","format":"uuid","description":"Unique event id — stable across redelivery attempts, dedupe on it."},"type":{"type":"string","enum":["conversation.created","conversation.updated","comment.created"]},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","required":["conversation"],"properties":{"conversation":{"$ref":"#/components/schemas/Conversation"},"comment":{"$ref":"#/components/schemas/Comment"}}}}},"Invitation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"nullable":true,"type":"string"},"role":{"type":"string","enum":["agent","admin"]},"language":{"type":"string"},"team_ids":{"nullable":true,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"invited_by_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"expires_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","email","name","role","language","team_ids","invited_by_id","expires_at","created_at"]},"Team":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"slug":{"type":"string","description":"URL-safe identifier, unique within the organization. Derived from the name at creation and never changes."},"description":{"nullable":true,"type":"string"},"color":{"nullable":true,"description":"Hex colour used for the team badge in the dashboard.","type":"string"},"icon":{"nullable":true,"type":"string"},"is_default":{"type":"boolean","description":"The organization's default team. Channels without an explicit team route here. Cannot be deleted."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Sort order in the dashboard sidebar (0-based)."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","name","slug","description","color","icon","is_default","position","created_at","updated_at"]},"TeamMember":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"nullable":true,"type":"string"},"role":{"type":"string","enum":["active","observer"],"description":"`active` members receive assignments and inbox notifications for the team; `observer` members can read the team inbox but are never auto-assigned."},"joined_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["user_id","email","name","role","joined_at"]},"Channel":{"type":"object","properties":{"id":{"type":"string","description":"The channel identifier as stamped on conversations (`channel_id`): the forwarding address for `email.postmark`, the connection UUID for synced mailboxes, the page id for `messenger` / `instagram` (one page yields one channel per enabled surface, same `id`, different `type`), the phone-number id for `whatsapp`, the widget UUID for `beacon`, the E.164 number for `voice`. `(type, id)` is the unique key."},"type":{"type":"string","enum":["email.postmark","email.gmail","email.microsoft","messenger","instagram","whatsapp","beacon","voice"],"description":"`email.postmark` = a forwarding address Elvo receives on its own inbound domain; `email.gmail` / `email.microsoft` = a shared mailbox synced over OAuth; `messenger` / `instagram` = a Meta page; `whatsapp` = a WhatsApp Business number; `beacon` = the website chat/contact widget (Spjallþjónn); `voice` = the phone line (Raddþjónn). Same vocabulary as `channel` on conversations."},"label":{"type":"string","description":"What the dashboard shows for this channel: the address, page name, @handle, number or widget name."},"team_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The team whose inbox receives this channel's conversations. Channels with no explicit routing land on the default team, so this is never null."},"status":{"type":"string","enum":["active","pending","error"],"description":"`active` = receiving messages. `pending` = connected but not yet confirmed (a forwarding address still waiting for its first forwarded email, a WhatsApp number mid-registration). `error` = the provider connection needs attention (expired OAuth token, forwarding failed) — the dashboard shows the same warning under Settings."},"address":{"nullable":true,"description":"The externally reachable address, when there is one: the email address, the WhatsApp or voice phone number, or the Instagram handle. Null for Messenger pages and beacons.","type":"string"},"display_name":{"nullable":true,"description":"Email only — the From name outgoing replies are sent with. Null for other channel types and when unset.","type":"string"},"is_primary":{"type":"boolean","description":"Email only — the team's primary sending address, used when a reply does not name one. At most one per team. Always false for non-email channels."},"auto_reply_enabled":{"type":"boolean","description":"Email only — whether the channel's automatic acknowledgement email is switched on. Always false for non-email channels."}},"required":["id","type","label","team_id","status","address","display_name","is_primary","auto_reply_enabled"]},"Tag":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"color":{"nullable":true,"type":"string","enum":["gray","red","orange","amber","green","teal","blue","indigo","purple","pink"]},"emoji":{"nullable":true,"type":"string"},"conversation_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"last_used_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","name","color","emoji","conversation_count","last_used_at","created_at","updated_at"]},"CannedResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"content":{"type":"string"},"category":{"nullable":true,"type":"string"},"team_ids":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"sort_order":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"exclude_from_ai":{"type":"boolean"},"created_by_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","title","content","category","team_ids","sort_order","exclude_from_ai","created_by_id","created_at","updated_at"]},"AutomationRule":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"description":{"nullable":true,"type":"string"},"is_active":{"type":"boolean"},"condition_logic":{"type":"string","enum":["all","any","none"],"description":"`all` — every condition must match (AND); `any` — at least one (OR); `none` — none may match (NOR)."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["from_email","from_domain","subject","body","channel","team","to_email","to_domain","cc_email","any_recipient","has_attachment","attachment_name","outside_opening_hours"]},"operator":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","ends_with","matches"]},"value":{"type":"string","minLength":1}},"required":["field","operator","value"],"description":"One condition. `field` names what is inspected on the inbound message (`from_email`, `from_domain`, `subject`, `body`, `channel`, `team`, `to_email`, `to_domain`, `cc_email`, `any_recipient`, `has_attachment`, `attachment_name`, `outside_opening_hours`); `operator` is how `value` is compared (`matches` is a regular expression). `has_attachment` and `outside_opening_hours` are booleans: `value` is `\"true\"` or `\"false\"` with operator `is` / `is_not`; `outside_opening_hours` refers to the organization opening hours and is never true for an organization without them."}},"actions":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["ignore"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_merge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_ignore"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["forward_email"]},"toEmail":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"includeAttachments":{"type":"boolean"}},"required":["type","toEmail"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_status"]},"status":{"type":"string","enum":["open","pending","resolved","ignored"]}},"required":["type","status"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to"]},"userIds":{"minItems":1,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]}},"required":["type","userIds"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to_team"]},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"roundRobin":{"type":"boolean"},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]},"userIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"onlyOnline":{"type":"boolean"}},"required":["type","teamId"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_contact_from_body"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["stop_processing_more_rules"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["snooze_ticket"]},"minutes":{"type":"integer","minimum":1,"maximum":525600}},"required":["type","minutes"]},{"type":"object","properties":{"type":{"type":"string","enum":["archive"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["auto_reply"]},"message":{"type":"string","minLength":1,"maxLength":10000}},"required":["type","message"]},{"type":"object","properties":{"type":{"type":"string","enum":["add_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]},{"type":"object","properties":{"type":{"type":"string","enum":["remove_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]}],"description":"One action, discriminated on `type`. Keys inside an action are camelCase (`toEmail`, `includeAttachments`, `userIds`, `teamId`, `roundRobin`, `onlyOnline`) — the one deliberate exception to snake_case on this API, because the action JSON is the rule engine's own format and is meant to round-trip verbatim through version control. `add_tag`/`remove_tag` carry tag names. `assign_to.userIds` and `assign_to_team.userIds` must be active members of the organization; `assign_to_team.teamId` must be one of the organization's teams."}},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Evaluation order — lower runs first."},"created_by_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","name","description","is_active","condition_logic","conditions","actions","priority","created_by_id","created_at","updated_at"]},"AutomationRuleCondition":{"type":"object","properties":{"field":{"type":"string","enum":["from_email","from_domain","subject","body","channel","team","to_email","to_domain","cc_email","any_recipient","has_attachment","attachment_name","outside_opening_hours"]},"operator":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","ends_with","matches"]},"value":{"type":"string","minLength":1}},"required":["field","operator","value"],"description":"One condition. `field` names what is inspected on the inbound message (`from_email`, `from_domain`, `subject`, `body`, `channel`, `team`, `to_email`, `to_domain`, `cc_email`, `any_recipient`, `has_attachment`, `attachment_name`, `outside_opening_hours`); `operator` is how `value` is compared (`matches` is a regular expression). `has_attachment` and `outside_opening_hours` are booleans: `value` is `\"true\"` or `\"false\"` with operator `is` / `is_not`; `outside_opening_hours` refers to the organization opening hours and is never true for an organization without them."},"AutomationRuleAction":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["ignore"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_merge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_ignore"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["forward_email"]},"toEmail":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"includeAttachments":{"type":"boolean"}},"required":["type","toEmail"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_status"]},"status":{"type":"string","enum":["open","pending","resolved","ignored"]}},"required":["type","status"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to"]},"userIds":{"minItems":1,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]}},"required":["type","userIds"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to_team"]},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"roundRobin":{"type":"boolean"},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]},"userIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"onlyOnline":{"type":"boolean"}},"required":["type","teamId"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_contact_from_body"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["stop_processing_more_rules"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["snooze_ticket"]},"minutes":{"type":"integer","minimum":1,"maximum":525600}},"required":["type","minutes"]},{"type":"object","properties":{"type":{"type":"string","enum":["archive"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["auto_reply"]},"message":{"type":"string","minLength":1,"maxLength":10000}},"required":["type","message"]},{"type":"object","properties":{"type":{"type":"string","enum":["add_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]},{"type":"object","properties":{"type":{"type":"string","enum":["remove_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]}],"description":"One action, discriminated on `type`. Keys inside an action are camelCase (`toEmail`, `includeAttachments`, `userIds`, `teamId`, `roundRobin`, `onlyOnline`) — the one deliberate exception to snake_case on this API, because the action JSON is the rule engine's own format and is meant to round-trip verbatim through version control. `add_tag`/`remove_tag` carry tag names. `assign_to.userIds` and `assign_to_team.userIds` must be active members of the organization; `assign_to_team.teamId` must be one of the organization's teams."},"AutomationRuleApplyResult":{"type":"object","properties":{"scanned":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Open/pending conversations evaluated (at most 500 per run)."},"matched":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Conversations whose first inbound message matched the conditions."},"applied":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Matched conversations that were actually changed."},"skipped":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Conversations that could not be evaluated (no inbound message) or matched but produced no change."},"capped":{"type":"boolean","description":"True when more than 500 open/pending conversations exist — only the oldest 500 were scanned. Narrow the rule or run again."}},"required":["scanned","matched","applied","skipped","capped"]},"KnowledgeSource":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"source_type":{"type":"string","enum":["manual","url","file","migrated_init_doc","google_doc","google_sheet"]},"status":{"type":"string","enum":["processing","ready","error"]},"error_message":{"nullable":true,"type":"string"},"chunk_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"source_url":{"nullable":true,"type":"string"},"source_file_name":{"nullable":true,"type":"string"},"last_synced_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"created_by_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","title","source_type","status","error_message","chunk_count","source_url","source_file_name","last_synced_at","created_by_id","created_at","updated_at"]},"KnowledgeSourceWithContent":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"source_type":{"type":"string","enum":["manual","url","file","migrated_init_doc","google_doc","google_sheet"]},"status":{"type":"string","enum":["processing","ready","error"]},"error_message":{"nullable":true,"type":"string"},"chunk_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"source_url":{"nullable":true,"type":"string"},"source_file_name":{"nullable":true,"type":"string"},"last_synced_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"created_by_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"content":{"nullable":true,"type":"string"}},"required":["id","title","source_type","status","error_message","chunk_count","source_url","source_file_name","last_synced_at","created_by_id","created_at","updated_at","content"]},"PerformanceReport":{"type":"object","properties":{"is_real_data":{"type":"boolean","description":"False when the organization has no agent-reply history yet (fewer than one week of replies in the last 90 days). Then `summary`, `trends`, `window` and `config` are omitted."},"low_confidence":{"description":"True when the organization has fewer than 4 weeks of reply history: the numbers are real but small samples make them noisy.","type":"boolean"},"generated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"When this payload was computed. Reports are cached for 5 minutes per scope, so this can be up to 5 minutes old."},"window":{"description":"The resolved reporting window and its comparison window, ISO-8601 UTC. Intervals are half-open [start, end): `end` is the midnight AFTER the last included day.","type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"prev_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"prev_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"comparison":{"type":"string","enum":["prevPeriod","prevYear"],"description":"Which comparison window `prev_start`/`prev_end` describe."}},"required":["start","end","prev_start","prev_end","comparison"]},"config":{"description":"Organization settings the metrics were computed with: business hours (for the `*_business` metrics), the minutes credited per accepted AI reply, the trend bucket size, and the org-set SLA targets.","type":"object","properties":{"business_hours":{"type":"object","properties":{"timezone":{"type":"string"},"start_hour":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"end_hour":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"workdays":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"ISO weekday numbers (1 = Monday … 7 = Sunday)."}},"required":["timezone","start_hour","end_hour","workdays"]},"minutes_per_accepted_reply":{"type":"number"},"trend_granularity":{"type":"string","enum":["day","week","month"]},"targets":{"type":"object","properties":{"first_response_minutes":{"nullable":true,"type":"number"},"resolution_minutes":{"nullable":true,"type":"number"}},"required":["first_response_minutes","resolution_minutes"],"description":"Organization-set SLA targets in minutes (null when unset). Lower is better: a metric meets its target when its value is at or under it. Applies to the selected time mode."}},"required":["business_hours","minutes_per_accepted_reply","trend_granularity","targets"]},"summary":{"type":"object","properties":{"tfr":{"type":"object","properties":{"current":{"type":"number","description":"Median for the window, in minutes."},"previous":{"nullable":true,"description":"Median for the comparison window, in minutes; null when it had no qualifying conversations.","type":"number"},"p90":{"type":"number","description":"90th percentile, in minutes."},"p95":{"type":"number","description":"95th percentile, in minutes."},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous","p90","p95"],"description":"Time to first reply (wall clock): minutes from the customer's first inbound message to the first agent reply."},"tfr_business":{"type":"object","properties":{"current":{"type":"number","description":"Median for the window, in minutes."},"previous":{"nullable":true,"description":"Median for the comparison window, in minutes; null when it had no qualifying conversations.","type":"number"},"p90":{"type":"number","description":"90th percentile, in minutes."},"p95":{"type":"number","description":"95th percentile, in minutes."},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous","p90","p95"],"description":"Time to first reply counted in business hours only (`config.business_hours`)."},"fcr":{"type":"object","properties":{"current":{"type":"number","description":"Percentage of resolved conversations closed with a single agent reply."},"previous":{"nullable":true,"type":"number"},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous"],"description":"First-contact resolution rate (percentage)."},"closed":{"type":"object","properties":{"current":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"previous":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous"],"description":"Conversations closed in the window and in the comparison window."},"created":{"type":"object","properties":{"current":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"previous":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous"],"description":"Conversations created in the window and in the comparison window."},"ai":{"type":"object","properties":{"approved":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"prev_approved":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"prev_total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["approved","total","prev_approved","prev_total"],"description":"AI suggestions (drafts, tags, triage) offered vs accepted by agents."},"ai_usage":{"type":"object","properties":{"drafts_offered":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"AI draft replies offered in the window."},"drafts_used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Offered drafts that were applied AND followed by a non-internal agent reply sent after applying."},"drafts_used_pct":{"type":"number","description":"`drafts_used` over the reply-warranted denominator (drafts on conversations that got an agent reply), as a percentage."},"toolbar":{"type":"array","items":{"type":"object","properties":{"operation":{"type":"string","description":"Composer AI-toolbar operation (e.g. `aidraft`, `proofread`, `translate`, `rewrite`, `tone`, `custom`)."},"runs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Times the operation was generated."},"applied":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Generated previews the agent accepted."}},"required":["operation","runs","applied"]},"description":"Composer AI-toolbar usage broken down by operation."}},"required":["drafts_offered","drafts_used","drafts_used_pct","toolbar"],"description":"AI adoption: how often offered draft replies were actually used, and composer AI-toolbar usage by operation."},"resolution":{"type":"object","properties":{"current":{"type":"number"},"previous":{"nullable":true,"type":"number"},"p90":{"type":"number"},"sample_size":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Conversations the resolution-time medians are computed over."},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous","p90","sample_size"],"description":"Time to resolution (wall clock, minutes) for conversations resolved by a human or the AI agent."},"resolution_business":{"type":"object","properties":{"current":{"type":"number"},"previous":{"nullable":true,"type":"number"},"p90":{"type":"number"},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous","p90"],"description":"Time to resolution counted in business hours only (minutes)."},"resolution_first":{"type":"object","properties":{"current":{"type":"number"}},"required":["current"],"description":"Median minutes to the FIRST resolution event, ignoring later reopens."},"reopen":{"type":"object","properties":{"rate_pct":{"nullable":true,"type":"number"},"previous_rate_pct":{"nullable":true,"type":"number"},"reopened_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"resolved_total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["rate_pct","previous_rate_pct","reopened_count","resolved_total"],"description":"Share of resolved conversations that were reopened (percentage; null when nothing was resolved)."},"backlog":{"type":"object","properties":{"open":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pending":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"oldest_minutes":{"nullable":true,"description":"Age of the oldest open conversation, in minutes.","type":"number"}},"required":["open","pending","oldest_minutes"],"description":"Point-in-time backlog at generation time (not window-scoped)."},"touchless":{"type":"object","properties":{"current":{"type":"number","description":"Percentage of closes handled by a machine."},"previous":{"nullable":true,"type":"number"},"machine_handled":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Machine-handled closes (resolved + deflected) — the numerator."},"closed_total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"All closes in the window (resolved + ignored) — the denominator."},"auto_resolved":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Machine-handled conversations that were resolved."},"deflected":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Machine-handled conversations that were ignored (deflected noise)."},"ai_agent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Resolved by the chatbot/voice agent."},"ai_intake":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Auto-resolved or deflected by intake AI (transactional, spam, auto-reply)."},"rule":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Closed by an automation or blocking rule."},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous","machine_handled","closed_total","auto_resolved","deflected","ai_agent","ai_intake","rule"],"description":"Machine-handled closes: conversations closed without a human touch, split by what handled them."},"ai_panel":{"type":"object","properties":{"involved_pct":{"type":"number"},"involved_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"ai_resolved_pct":{"type":"number"},"ai_resolved_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"peer_median":{"description":"Median of this metric across active paying Elvo customers; null when not available for this scope.","nullable":true,"type":"number"},"peer_sample_orgs":{"description":"How many organizations contributed to `peer_median`.","nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["involved_pct","involved_count","created_total","ai_resolved_pct","ai_resolved_count"],"description":"AI involvement: share of new conversations with at least one AI reply, and share of resolutions driven by AI."},"ai_hours_saved":{"type":"object","properties":{"current":{"type":"number"},"previous":{"type":"number"},"accepted_replies":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"minutes_per_reply":{"type":"number"}},"required":["current","previous","accepted_replies","minutes_per_reply"],"description":"Estimated agent hours saved = accepted AI draft replies × `minutes_per_reply` ÷ 60 (the per-reply minutes are an organization setting, see `config.minutes_per_accepted_reply`)."},"csat":{"type":"object","properties":{"current":{"nullable":true,"type":"number"},"previous":{"nullable":true,"type":"number"},"positive":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["current","previous","positive","total"],"description":"Customer satisfaction from chat ratings (thumbs up/down asked when a chat ends): share of ratings in the window that were positive. `current` is null when nothing was rated."}},"required":["tfr","tfr_business","fcr","closed","created","ai","ai_usage","resolution","resolution_business","resolution_first","reopen","backlog","touchless","ai_panel","ai_hours_saved","csat"],"description":"Headline metrics for the window. Latencies are minutes, rates are percentages, both rounded to one decimal. `previous` is null when the comparison window had no qualifying data."},"trends":{"type":"object","properties":{"tfr":{"type":"array","items":{"type":"object","properties":{"week":{"type":"string","description":"Bucket start as YYYY-MM-DD. Bucket width is `config.trend_granularity` (the key is `week` for historical reasons)."},"minutes":{"nullable":true,"type":"number"}},"required":["week","minutes"]}},"tfr_business":{"type":"array","items":{"type":"object","properties":{"week":{"type":"string","description":"Bucket start as YYYY-MM-DD. Bucket width is `config.trend_granularity` (the key is `week` for historical reasons)."},"minutes":{"nullable":true,"type":"number"}},"required":["week","minutes"]}},"volume":{"type":"array","items":{"type":"object","properties":{"week":{"type":"string","description":"Bucket start as YYYY-MM-DD. Bucket width is `config.trend_granularity` (the key is `week` for historical reasons)."},"created":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"closed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"prev_created":{"nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["week","created","closed","prev_created"]}},"ai":{"type":"array","items":{"type":"object","properties":{"week":{"type":"string","description":"Bucket start as YYYY-MM-DD. Bucket width is `config.trend_granularity` (the key is `week` for historical reasons)."},"suggestions":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"approved":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["week","suggestions","approved"]}},"ai_usage":{"type":"array","items":{"type":"object","properties":{"week":{"type":"string","description":"Bucket start as YYYY-MM-DD. Bucket width is `config.trend_granularity` (the key is `week` for historical reasons)."},"drafts_offered":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"drafts_used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"toolbar_runs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"toolbar_applied":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["week","drafts_offered","drafts_used","toolbar_runs","toolbar_applied"]}},"fcr":{"type":"array","items":{"type":"object","properties":{"week":{"type":"string","description":"Bucket start as YYYY-MM-DD. Bucket width is `config.trend_granularity` (the key is `week` for historical reasons)."},"rate":{"nullable":true,"type":"number"}},"required":["week","rate"]}},"channels":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string","description":"Channel label as shown in the dashboard (e.g. `email`, `chat`)."},"value":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Conversations created on this channel."},"tfr_minutes":{"nullable":true,"type":"number"},"tfr_business_minutes":{"nullable":true,"type":"number"}},"required":["channel","value","tfr_minutes","tfr_business_minutes"]}},"tfr_distribution":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","enum":["<5m","5-30m","30m-2h","2h-4h",">4h"]},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["bucket","count"]}},"resolution_distribution":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","enum":["<1h","1-8h","8-24h","1-3d","3-7d",">7d"]},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["bucket","count"]}},"tfr_distribution_business":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","enum":["<5m","5-30m","30m-2h","2h-4h",">4h"]},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["bucket","count"]}},"resolution_distribution_business":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","enum":["<1h","1-8h","8-24h","1-3d","3-7d",">7d"]},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["bucket","count"]}},"heatmap":{"type":"array","items":{"type":"object","properties":{"dow":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Day of week, 0 = Sunday … 6 = Saturday."},"hour":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Hour of day in the organization's business-hours timezone."},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["dow","hour","count"]}},"agent_replies":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"nullable":true,"type":"string"},"email":{"type":"string"},"image":{"nullable":true,"type":"string"},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["user_id","name","email","image","count"]}},"agent_stats":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"nullable":true,"type":"string"},"email":{"type":"string"},"image":{"nullable":true,"type":"string"},"reply_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tickets_handled":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tickets_closed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"median_tfr_minutes":{"nullable":true,"type":"number"},"median_resolution_minutes":{"nullable":true,"type":"number"},"median_tfr_business_minutes":{"nullable":true,"type":"number"},"median_resolution_business_minutes":{"nullable":true,"type":"number"},"fcr_rate":{"nullable":true,"type":"number"},"reopen_rate":{"nullable":true,"type":"number"},"csat_rate":{"nullable":true,"type":"number"},"csat_total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["user_id","name","email","image","reply_count","tickets_handled","tickets_closed","median_tfr_minutes","median_resolution_minutes","median_tfr_business_minutes","median_resolution_business_minutes","fcr_rate","reopen_rate","csat_rate","csat_total"]}},"team_stats":{"type":"array","items":{"type":"object","properties":{"team_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"color":{"nullable":true,"type":"string"},"icon":{"nullable":true,"type":"string"},"new_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"closed_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"median_tfr_minutes":{"nullable":true,"type":"number"},"median_resolution_minutes":{"nullable":true,"type":"number"},"median_tfr_business_minutes":{"nullable":true,"type":"number"},"median_resolution_business_minutes":{"nullable":true,"type":"number"},"fcr_rate":{"nullable":true,"type":"number"},"reopen_rate":{"nullable":true,"type":"number"},"csat_rate":{"nullable":true,"type":"number"},"csat_total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["team_id","name","color","icon","new_count","closed_count","median_tfr_minutes","median_resolution_minutes","median_tfr_business_minutes","median_resolution_business_minutes","fcr_rate","reopen_rate","csat_rate","csat_total"]}}},"required":["tfr","tfr_business","volume","ai","ai_usage","fcr","channels","tfr_distribution","resolution_distribution","tfr_distribution_business","resolution_distribution_business","heatmap","agent_replies","agent_stats","team_stats"],"description":"Time series and breakdowns. Series are bucketed by `config.trend_granularity`; distributions are histograms over the window; `agent_stats`/`team_stats` are per-agent and per-team rollups."}},"required":["is_real_data","generated_at"]},"OutOfOffice":{"type":"object","properties":{"starts_at":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"ends_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"message":{"type":"string"},"delegate_user_id":{"nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channels":{"type":"object","properties":{"chat":{"type":"boolean"},"email":{"type":"boolean"}},"required":["chat","email"]},"active":{"type":"boolean"}},"required":["starts_at","ends_at","message","delegate_user_id","channels","active"]},"Me":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"nullable":true,"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["agent","admin"],"description":"The token owner's role in the token's organization."}},"required":["id","name","email","role"],"description":"The member who created the token. Everything the token does is done as this user."},"organization":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["id","name","slug"],"description":"The organization the token is bound to. Every request is scoped to it."},"token":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"scope":{"type":"string","enum":["read","write"],"description":"`read` tokens may only GET; any other method is a 403."},"expires_at":{"nullable":true,"description":"`null` for a token that never expires.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","name","scope","expires_at"],"description":"The token used for this request."}},"required":["user","organization","token"]}}},"paths":{"/conversations":{"post":{"operationId":"createConversation","summary":"Create a conversation","description":"Creates a conversation through the same intake pipeline as the dashboard: the contact is found or created by email, automation rules run, and the conversation shows up in inboxes/notifications like any other.\n\n`message` is recorded as the **contact's** first message (inbound semantics) — nothing is sent to the contact. To send the contact something, create the conversation and then `POST /conversations/{id}/comments`.\n\n**Threading.** By default (`threading: \"never\"`) every call opens a fresh conversation and returns **201**. With `threading: \"auto\"`, the message runs the same dedup/threading as inbound channels: it may attach to an existing conversation from the same contact — open, pending, or resolved, from the last 30 days, on **any** channel — or to the conversation referenced by a `[#123]` marker in the subject. When that happens the response is **200** with the existing conversation, the message is appended to it, and your `status`/`assigned_to_id`/`team_id`/`tags` are **not** applied.","tags":["Conversations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"maxLength":200},"contact":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":50},"company":{"type":"string","maxLength":200}},"required":["email"],"description":"Found or created by email. Existing contacts keep their name; phone/company are updated when provided."},"message":{"type":"string","minLength":1,"maxLength":100000,"description":"Recorded as the contact's first message on the conversation."},"status":{"description":"Defaults to `open`.","type":"string","enum":["open","pending","resolved","ignored"]},"assigned_to_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"team_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"tags":{"description":"Tag names to apply. Tags are an org-wide vocabulary — an unknown name creates the tag on first use. Names are case-sensitive and must not contain commas (a comma splits the entry into several tags).","maxItems":50,"type":"array","items":{"type":"string","maxLength":100}},"channel":{"default":"webhook","description":"How the conversation is labelled in the dashboard.","type":"string","enum":["webhook","manual","phone"]},"threading":{"default":"never","description":"`never` always opens a fresh conversation. `auto` may attach the message to an existing conversation (see endpoint description).","type":"string","enum":["never","auto"]}},"required":["subject","contact","message"]},"examples":{"inject":{"summary":"Log a conversation from an external system","value":{"subject":"Order #1042 — damaged on arrival","contact":{"email":"jon@example.is","name":"Jón Jónsson"},"message":"My package arrived damaged, can you help?","tags":["orders"]}}}}}},"responses":{"200":{"description":"threading=auto matched an existing conversation — the message was appended to it","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"201":{"description":"The created conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"get":{"operationId":"listConversations","summary":"List conversations","description":"Newest first: sorted by `created_at` descending (then `id`), paged with `cursor`.\n\n**Defaults to know:**\n\n- No `status` filter means **every** status, including `ignored` (spam) and `deleted` (trash). For the working queue pass `status=open`.\n- `snoozed` defaults to `hide`: conversations snoozed into the future are left out.\n- `archived` defaults to `all`: archived conversations are included. Pass `archived=hide` to match the inbox.\n\nThe list is scoped to what the token's user can see: team conversations of the organization plus the user's own personal-mailbox conversations. Internal team chats are never listed here (see `/chats`).","tags":["Conversations"],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"status","in":"query","schema":{"type":"string","enum":["open","pending","resolved","ignored","deleted"]}},{"name":"assigned_to_id","in":"query","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},{"name":"team_id","in":"query","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},{"name":"contact_id","in":"query","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},{"name":"channel","in":"query","schema":{"type":"string"},"description":"Only conversations that arrived on this channel type — a `Channel.type` value from `GET /channels` (or `beacon_chat` / `beacon_form` for the two widget surfaces, `webhook` / `manual` / `phone` for API-created ones)."},{"name":"channel_id","in":"query","schema":{"type":"string"},"description":"Only conversations that arrived on this specific channel — a `Channel.id` from `GET /channels`. Usually combined with `channel`, since a Meta page id is shared by its Messenger and Instagram channels."},{"name":"tag","in":"query","schema":{"type":"string"},"description":"Only conversations carrying this tag. Exact name match, case-sensitive — see the `tags` field on Conversation."},{"name":"q","in":"query","schema":{"example":"reikningur","type":"string"},"description":"Search, the same engine as the dashboard search box: matches the subject, message bodies (including internal notes; quoted reply chains stripped, newest ~20,000 characters per conversation), attachment file names, and the contact's and other participants' names and email addresses. Accent-insensitive (`jon` matches `Jón`), word-prefix and substring matching; all words must match. `\"quoted phrase\"` matches the exact phrase; `#123` matches conversation number 123. Messages from the last minute or so may not be searchable yet (subject and contact still match)."},{"name":"contact_email","in":"query","schema":{"example":"jon@example.is","type":"string","minLength":1,"maxLength":320},"description":"Only conversations whose contact has this email address. Exact match, case-insensitive."},{"name":"created_after","in":"query","schema":{"example":"2026-09-01T00:00:00Z","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"description":"Only conversations created after this ISO 8601 timestamp. Returned timestamps are rounded to milliseconds, so a conversation stamped in the same millisecond can come back again; dedupe by `id`."},{"name":"updated_after","in":"query","schema":{"example":"2026-09-28T12:00:00Z","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"description":"Only conversations whose `updated_at` is after this ISO 8601 timestamp (returned timestamps are rounded to milliseconds, so the boundary one can repeat). For change polling use `sort=updated_at&order=asc`, pass the largest `updated_at` you have seen minus a few minutes of overlap, and dedupe by `id` (inbound email carries its sent time, which can precede its arrival)."},{"name":"sort","in":"query","schema":{"default":"created_at","example":"updated_at","type":"string","enum":["created_at","updated_at"]},"description":"Sort key. `created_at` (default) or `updated_at`. A `cursor` is only valid with the `sort` and `order` it was issued for."},{"name":"order","in":"query","schema":{"default":"desc","example":"asc","type":"string","enum":["desc","asc"]},"description":"Sort direction. `desc` (default, newest first) or `asc` (oldest first)."},{"name":"snoozed","in":"query","schema":{"type":"string","enum":["hide","only","all"]},"description":"Snooze filter. `hide` (default) excludes snoozed conversations, `only` returns just snoozed, `all` returns both."},{"name":"archived","in":"query","schema":{"example":"hide","type":"string","enum":["hide","only","all"]},"description":"Archive filter. `all` (default) returns both, `hide` excludes archived conversations (what the inbox shows), `only` returns just archived."},{"name":"unanswered_for_minutes","in":"query","schema":{"type":"integer","minimum":1,"maximum":100000},"description":"Only open/pending conversations whose most recent inbound customer message has had no non-internal human reply for longer than this many minutes. Also bounded to those with a customer message in the last max(4× the threshold, 30 days)."}],"responses":{"200":{"description":"Paginated list of conversations","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/conversations/drafts":{"post":{"operationId":"createConversationDraft","summary":"Create an outbound compose draft","description":"Creates an empty outbound draft conversation — the API twin of the dashboard's \"new conversation\" compose flow. No contact and no messages exist yet, and nothing is delivered.\n\nUse `send_from_email` to pick the sending address: a personal mailbox connection anchors the draft to that agent's personal inbox, a team address anchors it to the team, and an omitted or unrecognized address falls back to the org default team (check `team_id` on the response).\n\nThen stage the email with `PUT /conversations/{conversationId}/draft` (body, `to`, `subject`, `attachment_ids`, `mode: \"new\"`) so a human can review and send it from the composer. Sending remains `POST /conversations/{conversationId}/comments`. Compose drafts left completely untouched by the same creator are garbage-collected on their next compose, matching the dashboard.","tags":["Conversations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"maxLength":200},"send_from_email":{"description":"Connected address to compose from (personal mailbox or team address).","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}},"required":["subject"]},"examples":{"personalCompose":{"summary":"Compose from a personal mailbox","value":{"subject":"Contract draft","send_from_email":"agent@example.com"}}}}}},"responses":{"201":{"description":"The created draft conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/conversations/{conversationId}":{"get":{"operationId":"getConversation","summary":"Get a conversation","description":"Look up by `id` (uuid) or by `conversation_number` (digits only, the `#123` shown in the dashboard). Every other conversation endpoint takes the uuid `id` from this response.","tags":["Conversations"],"parameters":[{"name":"conversationId","in":"path","required":true,"description":"Conversation `id` (uuid), or its `conversation_number` (digits only).","schema":{"oneOf":[{"type":"string","format":"uuid"},{"type":"string","pattern":"^\\d{1,9}$"}]},"example":"1234"}],"responses":{"200":{"description":"The conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateConversation","summary":"Update a conversation","description":"Update one or more of: subject, status, tags, assignee, team. Tags either replace the whole set (`tags`) or change it incrementally (`add_tags` / `remove_tags`, not combinable with `tags`). Status transitions go through the same pipeline as the dashboard (resolvedAt is stamped, snoozes are cleared, empty deleted tickets are hard-deleted). All fields are optional — pass only what you want to change. Note: when a status change triggers a hard delete, the response is `{\"data\": {\"id\", \"status\": \"deleted\", \"hard_deleted\": true}}` instead of the full conversation.","tags":["Conversations"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"maxLength":200},"status":{"type":"string","enum":["open","pending","resolved","ignored","deleted"]},"assigned_to_id":{"description":"User ID to assign, or null to unassign.","nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"team_id":{"description":"Team ID to move the conversation to, or null to remove it from any team. On a personal-mailbox conversation, setting a team SHARES it: the whole thread becomes visible to the team and the conversation keeps threading with the personal mailbox. Setting null on a shared personal-mailbox conversation UNSHARES it back to the owner's private view — allowed only for the owner or an org admin (403 otherwise). Team-origin conversations cannot become personal.","nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"tags":{"description":"Full replacement of the conversation's tags, by name (an empty array clears them). Tags are an org-wide vocabulary — an unknown name creates the tag on first use. Names are case-sensitive and must not contain commas (a comma splits the entry into several tags).","maxItems":50,"type":"array","items":{"type":"string","maxLength":100}},"add_tags":{"description":"Tag names to add, keeping the existing tags. Names already on the conversation (case-insensitive) are ignored. Tags are an org-wide vocabulary — an unknown name creates the tag on first use. Names are case-sensitive and must not contain commas (a comma splits the entry into several tags).","example":["vip"],"maxItems":50,"type":"array","items":{"type":"string","maxLength":100}},"remove_tags":{"description":"Tag names to remove, keeping the other tags. Matched case-insensitively; names not on the conversation are ignored.","example":["needs-reply"],"maxItems":50,"type":"array","items":{"type":"string","maxLength":100}}},"description":"Cross-field rules: at least one field must be provided — an empty object is rejected; `tags` (full replacement) cannot be combined with `add_tags` / `remove_tags`; and one name cannot be in both `add_tags` and `remove_tags`. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"},"examples":{"resolve":{"summary":"Resolve a conversation","value":{"status":"resolved"}},"triage":{"summary":"Tag and assign","value":{"tags":["billing","vip"],"assigned_to_id":"5b1f3df4-3e76-4c1f-9b4d-2b1c3a8e5d12"}},"addRemoveTags":{"summary":"Add and remove tags, keeping the rest","value":{"add_tags":["vip"],"remove_tags":["needs-reply"]}}}}}},"responses":{"200":{"description":"The updated conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/conversations/{conversationId}/snooze":{"post":{"operationId":"snoozeConversation","summary":"Snooze a conversation","description":"Hides the conversation from default inbox views until `until`. A new non-system comment on the conversation wakes it automatically if `discardOnReply` is true (the default).","tags":["Conversations"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"until":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"ISO 8601 timestamp. Must be in the future."},"discardOnReply":{"description":"Wake the conversation when any non-system comment is created. Defaults to `true`.","type":"boolean"},"leaveInInbox":{"description":"Keep the conversation visible in inbox views instead of hiding it. Defaults to `false`.","type":"boolean"}},"required":["until"]}}}},"responses":{"200":{"description":"The updated conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"unsnoozeConversation","summary":"Unsnooze a conversation","description":"Immediately clears any active snooze. No-op if the conversation is not snoozed.","tags":["Conversations"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The updated conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/conversations/{conversationId}/comments":{"get":{"operationId":"listComments","summary":"List conversation comments","description":"Returns customer messages and agent replies in reverse-chronological order (newest first). Each comment includes its `attachments` (files/screenshots). Internal notes are hidden by default: pass `include_internal=true` to see them too, and `include_events=true` for the timeline events between them. An AI agent usually wants both for full context.\n\n**Reading the body.** `content` is the stored body, verbatim; `content_format` says whether it is `html` or `text`. `content_text` is a plain-text rendering with the quoted reply history removed from customer-visible messages, which is what an LLM should read. Email messages also carry `from`, `to` and `cc` when they were recorded.\n\n**Events.** In each event, `type`, `actor_*` and `created_at` are stable. `before`, `after` and `metadata` are internal, unstable payloads (camelCase keys that can change in any release). Do not build logic on them.","tags":["Comments"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"include_internal","in":"query","description":"When `true`, internal notes are included alongside customer-visible messages. Default `false`.","schema":{"type":"boolean","default":false}},{"name":"include_events","in":"query","description":"When `true`, the response carries a top-level `events` array: the timeline events (status, assignment, tag, snooze, merge, AI actions) that fall within this page of messages, oldest first. Combine with `include_internal=true` on conversations to rebuild the full dashboard timeline in one call.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Paginated list of comments","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Comment"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}},"events":{"type":"array","items":{"$ref":"#/components/schemas/TimelineEvent"},"description":"Present only when `include_events=true`. Timeline events within this page's time span, oldest first."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createComment","summary":"Send a reply or add an internal note","description":"Creates a new comment on the conversation.\n\n- **Reply** (`is_internal: false`, default) — delivered on the conversation's channel: email conversations send an email (**`to` is required** — the server never derives recipients, so name them explicitly, e.g. the sender of the message you are answering; this also applies to `voice` and channel-less conversations, whose replies deliver as email), `messenger`/`instagram` conversations send a Meta DM, and `beacon_chat` conversations reach the visitor in the chat widget (falling back to email when the visitor has left). If the conversation was `resolved`, `ignored`, or `deleted`, sending a reply automatically reopens it (or pass an explicit `status` to land on a specific status).\n- **Internal note** (`is_internal: true`) — visible to your team only. Does not send anything and does not change conversation status.\n\n**Content format differs by kind.** A reply may be plain text or HTML (it renders in email clients). An internal note is **plain text** with a small markdown subset — `**bold**`, `*italic*`, `` `code` ``, and bare URLs (auto-linked) — never HTML; HTML submitted on a note is converted to plain text server-side.\n\n**Mentions (notes only).** Write `@[Name]` (a teammate's exact display name), `@[team:Name]`, or `@[everyone]` (admin tokens only) in the note content — matching is case-insensitive and whitespace-tolerant. Mentioned teammates are notified and start watching the conversation, exactly as if mentioned from the dashboard. Names that don't match anyone still render as a mention chip but notify no one. In a reply, `@[...]` is literal text.\n\n**Limitations in v1.2:**\n\n- Scheduled-send and forward mode are not yet exposed via the public API. Use the dashboard for those.","tags":["Comments"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","description":"Comment body. For a reply: plain text or HTML (sanitized server-side). For an internal note (`is_internal: true`): plain text with markdown (`**bold**`, `*italic*`, `` `code` ``, bare URLs) — HTML is converted to plain text. Notes may mention teammates: `@[Name]`, `@[team:Name]`, `@[everyone]`."},"is_internal":{"default":false,"description":"`true` for an internal note (no email, no status change). `false` for a customer reply.","type":"boolean"},"status":{"description":"Optional explicit status to apply alongside the reply (e.g. resolve while replying).","type":"string","enum":["open","pending","resolved","ignored","deleted"]},"to":{"description":"Recipient list. Required for replies on email conversations — the server sends exactly what you specify and never falls back to the conversation contact. Each entry is `email@example.com` or `Name <email@example.com>`.","maxItems":50,"type":"array","items":{"type":"string"}},"cc":{"maxItems":50,"type":"array","items":{"type":"string"}},"bcc":{"maxItems":50,"type":"array","items":{"type":"string"}},"send_from_email":{"description":"Sender address for an email reply. Must be an address the acting user may send from (a team address or their own connected mailbox); otherwise 403 `send_from_not_allowed`. Omit to use the conversation's default address.","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"attachment_ids":{"description":"Attachment IDs from `POST /attachments` to include with the reply. Ids that do not exist, belong to another organization, or are already linked to a comment are silently skipped.","maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"quoted_comment_id":{"description":"Comment ID being quoted/replied-to (for thread context).","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["content"],"description":"Cross-field rule: `content` or `attachment_ids` must be non-empty — an empty message is rejected. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"},"examples":{"reply":{"summary":"Send a reply and resolve","value":{"content":"Thanks for reaching out — I've issued a refund.","status":"resolved"}},"note":{"summary":"Add an internal note","value":{"content":"Customer is on the Pro plan, prioritising.","is_internal":true}}}}}},"responses":{"201":{"description":"The new comment","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Comment"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/conversations/{conversationId}/comments/{commentId}":{"patch":{"operationId":"updateComment","summary":"Edit an internal note","description":"Replaces an internal note's `content`. Only notes the token's user wrote can be edited; anyone else's, and customer-facing replies (already sent), return 403. Quoting and attachments are fixed at creation and cannot be changed.\n\nA conversation the token cannot see — or a note that does not exist (including deleted ones) — returns 404.","tags":["Comments"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"commentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":10000,"description":"Replacement note body. Plain text with markdown, same format as when posting — HTML is converted to plain text."}},"required":["content"]},"examples":{"edit":{"summary":"Add a detail","value":{"content":"Customer confirmed by phone — closing tomorrow. (edited)"}}}}}},"responses":{"200":{"description":"The updated note","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Comment"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteComment","summary":"Delete an internal note","description":"Deletes an internal note. Only notes the token's user wrote can be deleted; anyone else's, and customer-facing replies, return 403. Soft delete — the note disappears from the timeline and the audit log keeps the content.\n\nA conversation the token cannot see — or a note that does not exist or is already deleted — returns 404.","tags":["Comments"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"commentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Note deleted","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/conversations/{conversationId}/draft":{"get":{"operationId":"getDraft","summary":"Get your draft on the conversation","description":"Returns the token user's own unsent reply draft on the conversation, or `null` if that user has none. A draft is not delivered: it surfaces in the helpdesk composer for a human to review and send. The dashboard keeps one draft per person on a conversation; drafts that teammates are writing are never returned here.","tags":["Comments"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The current draft, or null","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Draft"}],"nullable":true}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"put":{"operationId":"upsertDraft","summary":"Create or update your draft on the conversation","description":"Creates or updates a reply draft on the conversation (stages content for a human to review in the composer). This does **not** send anything; call `POST /conversations/{conversationId}/comments` to deliver.\n\n**The body is normalised to HTML.** Plain text is converted to paragraphs (a blank line starts a new `<p>`, a single newline becomes `<br>`), and in HTML an empty spacer paragraph is inserted between two adjacent paragraphs so the composer shows the blank line. So `\"Hi\"` is stored as `<p>Hi</p>`, and `GET` returns the normalised form, not your exact input. Email styling is applied later, at send time. Send an empty `content` to clear the draft body without removing the row; use `DELETE` to remove it entirely.\n\n**Only your own draft.** The dashboard keeps one draft per person on a conversation, and this endpoint works on the token user's draft only: it updates that draft when it exists (staged recipients you do not send are kept) and creates it otherwise. A teammate's draft on the same conversation is never read or changed. Everyone who opens the conversation in the dashboard sees the draft with the token user as its author, so a teammate can review, edit and send it. Teammates who have the conversation open see the new text live. A composer the token user already has open on that draft in the dashboard keeps its own text until the conversation is reopened, and typing there saves over yours.\n\nOptionally stage recipients (`to`/`cc`/`bcc`) and a `mode` so the composer opens the draft fully addressed — e.g. `mode: \"reply_all\"` with the original sender in `to` and others in `cc`. Omit them to let the composer derive recipients from the conversation. Each entry is an email, optionally `\"Name <email@host>\"`.\n\nAttachments: upload files via `POST /attachments`, then stage the returned ids in `attachment_ids` — the composer opens the draft with the files attached, and they are linked to the outbound email at send time. An empty array clears staged attachments; omitting the field leaves them untouched.","tags":["Comments"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","description":"Draft body. Plain text or HTML."},"to":{"description":"TO recipients to stage on the draft.","type":"array","items":{"type":"string"}},"cc":{"description":"CC recipients to stage on the draft.","type":"array","items":{"type":"string"}},"bcc":{"description":"BCC recipients to stage on the draft.","type":"array","items":{"type":"string"}},"mode":{"description":"Composer mode the draft opens in.","type":"string","enum":["reply","reply_all","forward","new"]},"subject":{"description":"Subject to stage (used by forward/new mode).","type":"string"},"attachment_ids":{"description":"Attachment ids (from POST /attachments) to stage on the draft. Empty array clears; omit to leave untouched.","maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["content"]},"examples":{"draft":{"summary":"Stage a reply for review","value":{"content":"Hi — here's a proposed answer for you to review before sending."}},"replyAll":{"summary":"Stage a reply-all addressed to the original sender, others on CC","value":{"content":"Quick follow-up question…","mode":"reply_all","to":["support@vendor.com"],"cc":["sales@vendor.com"]}}}}}},"responses":{"200":{"description":"The updated draft","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Draft"}}}}}},"201":{"description":"The newly created draft","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Draft"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteDraft","summary":"Delete your draft on the conversation","description":"Removes the token user's own reply draft on the conversation. Drafts that teammates are writing are never removed. Idempotent: succeeds even when the token user has no draft.","tags":["Comments"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Draft deleted","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/chats":{"get":{"operationId":"listChats","summary":"List your chats","description":"Every internal chat the token's user participates in — DMs, group chats, and any team/org channels they belong to — newest activity first.\n\nThis is the **only** way to discover chat IDs: chats are excluded from `/conversations` and never fire webhooks. Returns the full set (no pagination); a user's chat membership is inherently small.\n\n**Two-axis model — how internal messaging works:**\n\n|  | Thread is with… | Customer can see it? |\n|---|---|---|\n| External reply | a customer (`/conversations`) | yes |\n| Internal note | a customer (`/conversations`) | **no** — `is_internal: true` |\n| Chat message | teammates (`/chats`) | n/a — no customer involved |\n\n`is_internal` answers \"can the customer see this comment\" and only applies on a customer conversation. A chat is a thread *with teammates*, so it has no `is_internal` axis — everything in it is internal. An internal note and a DM are both team-only text but live on different thread kinds.","tags":["Chats"],"responses":{"200":{"description":"The chats you participate in","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Chat"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/chats/{chatId}":{"get":{"operationId":"getChat","summary":"Get a chat","description":"One chat the token's user can access. A chat you are not a member of returns 404 (existence is never revealed).","tags":["Chats"],"parameters":[{"name":"chatId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The chat","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Chat"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/mentions":{"get":{"operationId":"listMentions","summary":"List my mentions","description":"Every `@[Name]` mention of the token's user across customer conversations and internal chats, newest first. Backed by the dashboard bell notifications, so it is exactly what the user was tagged in. Filter with `since` (ISO-8601), `author_id`, or `kind`. Follow up with `GET /conversations/{id}/comments?include_internal=true` or `GET /chats/{id}/messages` for the surrounding thread.","tags":["Mentions"],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"since","in":"query","schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"description":"Only mentions created after this ISO-8601 instant."},{"name":"author_id","in":"query","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"description":"Only mentions written by this user."},{"name":"kind","in":"query","schema":{"type":"string","enum":["conversation","chat"]},"description":"Only customer conversations or only internal chats. Default: both."}],"responses":{"200":{"description":"Paginated list of mentions","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Mention"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/chats/{chatId}/messages":{"get":{"operationId":"listChatMessages","summary":"List chat messages","description":"Messages in a chat, reverse-chronological. Each message includes its `attachments` (files/screenshots) and `quoted_comment_id` (the message it quotes/replies to, if any). A chat you are not a member of returns 404.","tags":["Chats"],"parameters":[{"name":"chatId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"include_events","in":"query","description":"When `true`, the response carries a top-level `events` array: the timeline events (status, assignment, tag, snooze, merge, AI actions) that fall within this page of messages, oldest first. Combine with `include_internal=true` on conversations to rebuild the full dashboard timeline in one call.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Paginated list of chat messages","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}},"events":{"type":"array","items":{"$ref":"#/components/schemas/TimelineEvent"},"description":"Present only when `include_events=true`. Timeline events within this page's time span, oldest first."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createChatMessage","summary":"Post a chat message","description":"Posts a message to an internal chat. Delivered to participants in real time (no email, no status change). There is no `is_internal` flag — a chat message is internal by definition.\n\n**Chat messages are plain text, not HTML.** A small markdown subset renders: `**bold**`, `*italic*`, `` `code` ``, and bare URLs (auto-linked). HTML submissions are converted to plain text server-side.\n\n**Mentions.** Write `@[Name]` (a teammate's exact display name) in the message to notify that teammate — matching is case-insensitive and whitespace-tolerant. Team and `@[everyone]` mentions only apply on conversation notes, not chats.\n\nAttach screenshots/files by uploading via `POST /attachments` and passing the returned ids as `attachment_ids`. Unlike conversation replies, invalid/foreign/already-used attachment ids are **rejected** (not silently dropped), so a screenshot never goes missing.\n\nYou can only post to a chat you are a member of; otherwise 404.","tags":["Chats"],"parameters":[{"name":"chatId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","description":"Message body. Plain text with markdown (`**bold**`, `*italic*`, `` `code` ``, bare URLs auto-linked). HTML is converted to plain text. Mention a teammate with `@[Name]` to notify them."},"attachment_ids":{"description":"Attachment IDs from `POST /attachments` to include. All must exist, belong to your organization, and be unattached, or the request is rejected.","maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"quoted_comment_id":{"description":"A message id to quote/reply-to for thread context.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["content"],"description":"Cross-field rule: `content` or `attachment_ids` must be non-empty — an empty message is rejected. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"},"examples":{"message":{"summary":"Send a chat message","value":{"content":"Pushed the fix — can you review?"}}}}}},"responses":{"201":{"description":"The new chat message","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ChatMessage"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/chats/{chatId}/messages/{messageId}":{"patch":{"operationId":"updateChatMessage","summary":"Edit a chat message","description":"Replaces the message's `content`. You can only edit messages the token's user wrote; anyone else's return 403. Quoting and attachments are fixed at creation and cannot be changed.\n\nA chat you are not a member of — or a message that does not exist (including deleted ones) — returns 404.","tags":["Chats"],"parameters":[{"name":"chatId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":10000,"description":"Replacement message body. Plain text with markdown, same format as when posting — HTML is converted to plain text."}},"required":["content"]},"examples":{"edit":{"summary":"Fix a typo","value":{"content":"Pushed the fix — can you review? (edited)"}}}}}},"responses":{"200":{"description":"The updated chat message","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ChatMessage"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteChatMessage","summary":"Delete a chat message","description":"Deletes a message from a chat. You can only delete messages the token's user wrote; anyone else's return 403. The message disappears for all participants (soft delete — the audit log keeps the content).\n\nA chat you are not a member of — or a message that does not exist or is already deleted — returns 404.","tags":["Chats"],"parameters":[{"name":"chatId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Message deleted","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/contacts":{"get":{"operationId":"listContacts","summary":"List contacts","tags":["Contacts"],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"q","in":"query","schema":{"type":"string"},"description":"Search contacts by name or email (partial match)."},{"name":"email","in":"query","schema":{"example":"jon@example.is","type":"string","minLength":1},"description":"Exact email match, case-insensitive."}],"responses":{"200":{"description":"Paginated list of contacts","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/contacts/{contactId}":{"get":{"operationId":"getContact","summary":"Get a contact","tags":["Contacts"],"parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The contact","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/organization":{"get":{"operationId":"getOrganization","summary":"Get current organization","tags":["Organization"],"responses":{"200":{"description":"The organization","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Organization"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/attachments":{"post":{"operationId":"createAttachment","summary":"Upload an attachment","description":"Upload a file as `multipart/form-data` (field name: `file`). The returned `id` can then be passed in `attachment_ids` when creating a comment — an attachment can be linked to one comment, by the same organization that uploaded it.\n\nLimits: 4 MB per file (requests above the platform's ~4.5 MB body limit are rejected with a plain 413 before reaching the API). Executable file types are rejected. The returned `url` is an unauthenticated public URL.","tags":["Attachments"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"201":{"description":"The uploaded attachment","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Attachment"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhook endpoints","description":"Returns all webhook endpoints for the organization (max 10, no pagination). Signing secrets are never included.","tags":["Webhooks"],"responses":{"200":{"description":"All webhook endpoints","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createWebhook","summary":"Create a webhook endpoint","description":"Registers a URL to receive signed event payloads (see the **Webhooks** section above for payload format, signature verification, and retry semantics). The response is the only place the signing `secret` ever appears. Destination must be a public https URL, unique per organization (409 on duplicates). Max 10 endpoints per organization.","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000,"description":"Public https URL to POST events to. Unique per organization."},"events":{"description":"Event types to subscribe to. Duplicates are removed rather than rejected.","minItems":1,"maxItems":3,"type":"array","items":{"type":"string","enum":["conversation.created","conversation.updated","comment.created"]}},"description":{"type":"string","maxLength":500}},"required":["url","events"]},"examples":{"create":{"summary":"Subscribe to new conversations and comments","value":{"url":"https://example.com/elvo-webhook","events":["conversation.created","comment.created"],"description":"Production integration"}}}}}},"responses":{"201":{"description":"The created endpoint, including its signing secret (shown once)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"402":{"description":"Webhooks are not included in the organization's current plan (`error.type` is `plan_limit`; `error.details` carries `{feature, limit, current, plan}`). Existing endpoints keep delivering — only creating new ones is blocked.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"409":{"description":"An endpoint with this URL already exists","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/webhooks/{webhookId}":{"get":{"operationId":"getWebhook","summary":"Get a webhook endpoint","tags":["Webhooks"],"parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The webhook endpoint","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateWebhook","summary":"Update a webhook endpoint","description":"Update url, subscribed events, description, or `active`. Setting `active: true` on a disabled endpoint re-enables it and resets its failure counter. Changing `url` redirects queued deliveries to the new URL but does not reset the failure counter.","tags":["Webhooks"],"parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000},"events":{"description":"Event types to subscribe to. Duplicates are removed rather than rejected.","minItems":1,"maxItems":3,"type":"array","items":{"type":"string","enum":["conversation.created","conversation.updated","comment.created"]}},"description":{"description":"Set to null to clear.","nullable":true,"type":"string","maxLength":500},"active":{"type":"boolean"}},"description":"Cross-field rule: at least one field must be provided — an empty object is rejected. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"}}}},"responses":{"200":{"description":"The updated endpoint","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook endpoint","description":"Stops all deliveries immediately. In-flight deliveries to the deleted endpoint are dropped.","tags":["Webhooks"],"parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/users":{"get":{"operationId":"listUsers","summary":"List team members","description":"Newest first. Active members only unless `include_deactivated=true`; a deactivated member has a non-null `deactivated_at`. `team_ids` lists the teams the member belongs to in this organization.","tags":["Users"],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"include_deactivated","in":"query","schema":{"default":"false","type":"string","enum":["true","false"]},"description":"Include deactivated members. Default `false` returns active members only; deactivated members carry a non-null `deactivated_at`."}],"responses":{"200":{"description":"Paginated list of users","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/User"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/users/{userId}":{"get":{"operationId":"getUser","summary":"Get a team member","description":"Returns the member whether active or deactivated — check `deactivated_at`. 404 only when the user is not a member of this organization at all.","tags":["Users"],"parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The user","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/User"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateUser","summary":"Change a member's role","description":"Sets the member's organization role. Requires a token created by an organization admin (the token acts as the member who created it; a non-admin token gets 403). Two rules, each a 400: the token's own member cannot change their own role, and the last active admin cannot be demoted. 409 when the member is deactivated (reactivate first — the role can then be changed); 404 when the user is not a member of this organization. Takes effect on the member's own API tokens immediately.","tags":["Users"],"parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["agent","admin"],"description":"The member's organization role. `admin` can manage members, teams, billing and the AI knowledge; `agent` handles conversations."}},"required":["role"]},"examples":{"promote":{"summary":"Make a member an admin","value":{"role":"admin"}}}}}},"responses":{"200":{"description":"The updated user","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/User"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deactivateUser","summary":"Deactivate a member","description":"Soft-deactivates the member: they can no longer sign in and stop holding a seat, but their id, assignments and comments remain, and `POST /users/{userId}/reactivate` restores them. Not a row delete. Requires a token created by an organization admin (the token acts as the member who created it; a non-admin token gets 403). 400 when deactivating yourself or the last active admin; 404 when not an active member (already deactivated included). The member's own API tokens stop working immediately.","tags":["Users"],"parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deactivation confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/users/{userId}/reactivate":{"post":{"operationId":"reactivateUser","summary":"Reactivate a deactivated member","description":"Undoes a deactivation. The member takes a seat again (402 `plan_limit` if the plan's seat cap is reached) and receives a password-reset email so they can sign back in; the reactivation succeeds even if that email fails. Requires a token created by an organization admin (the token acts as the member who created it; a non-admin token gets 403). 404 when the user is not a member or is already active. No request body.","tags":["Users"],"parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The reactivated user","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/User"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"402":{"description":"Plan limit reached — `details` carries `{ feature, limit, current, plan }`","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/invitations":{"get":{"operationId":"listInvitations","summary":"List pending invitations","description":"Invitations that have not been accepted and have not expired, oldest first. Invitations expire 7 days after creation; expired ones disappear from this list and can simply be re-created. The invite token is never included.","tags":["Invitations"],"responses":{"200":{"description":"All pending invitations","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createInvitation","summary":"Invite a member","description":"Creates an invitation and emails the invitee a link (valid 7 days). On acceptance they become a User with `role` and join `team_ids` — every team in the organization when omitted. Requires a token created by an organization admin (the token acts as the member who created it; a non-admin token gets 403). 409 when the email already belongs to an active member, to a deactivated member (use `POST /users/{userId}/reactivate`, the response names the id), or to a pending invitation. 402 `plan_limit` when the plan's seat cap — current members plus pending invitations — is reached. 400 when a team id does not belong to the organization.","tags":["Invitations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":255,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","description":"Email address to invite. Normalized to lowercase; must not belong to a current member or a pending invitation."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The invitee's name, used to personalize the invitation email."},"role":{"type":"string","enum":["agent","admin"],"description":"Organization role the invitee gets on acceptance."},"language":{"description":"Language of the invitation email. Defaults to the inviting member's language.","type":"string","enum":["is","en"]},"team_ids":{"description":"Teams the invitee joins on acceptance. Every id must belong to the organization. Omit to add them to every team.","minItems":1,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["email","name","role"]},"examples":{"invite":{"summary":"Invite an agent to two teams","value":{"email":"anna@example.is","name":"Anna Jónsdóttir","role":"agent","language":"is","team_ids":["3fa85f64-5717-4562-b3fc-2c963f66afa6","7c9e6679-7425-40de-944b-e07fc1f90ae7"]}}}}}},"responses":{"201":{"description":"The created invitation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Invitation"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"402":{"description":"Plan limit reached — `details` carries `{ feature, limit, current, plan }`","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"409":{"description":"The email already belongs to a member (active or deactivated) or to a pending invitation","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/invitations/{invitationId}":{"delete":{"operationId":"deleteInvitation","summary":"Revoke an invitation","description":"Deletes the invitation; its link and short code stop working immediately. Requires a token created by an organization admin (the token acts as the member who created it; a non-admin token gets 403). 404 when no invitation with this id exists in the organization.","tags":["Invitations"],"parameters":[{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/teams":{"get":{"operationId":"listTeams","summary":"List teams","description":"Returns every team in the organization, ordered by `position` then name. No pagination — teams are capped by plan. Any member can list teams; membership gates a team's inbox, not its existence.","tags":["Teams"],"responses":{"200":{"description":"All teams","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Team"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createTeam","summary":"Create a team","description":"Creates a team. The `slug` is derived from the name and made unique within the organization (`support`, `support-2`, …); `position` goes after every existing team; when `color` is omitted one is picked from the default palette. Requires a token created by an organization admin (the token acts as its creator; a non-admin token gets 403). Blocked with 402 when the plan's team cap is reached.","tags":["Teams"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name. The `slug` is derived from it."},"description":{"type":"string","maxLength":500},"color":{"description":"Hex colour for the team badge. Omitted: one is picked from the default palette by position.","type":"string","maxLength":50},"icon":{"type":"string","maxLength":50}},"required":["name"]},"examples":{"create":{"summary":"A second-line support team","value":{"name":"Tæknisvið","description":"Escalations from first-line support","color":"#3B82F6"}}}}}},"responses":{"201":{"description":"The created team","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Team"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"402":{"description":"Plan limit reached — `details` carries `{ feature, limit, current, plan }`","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/teams/{teamId}":{"get":{"operationId":"getTeam","summary":"Get a team","tags":["Teams"],"parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The team","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Team"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateTeam","summary":"Update a team","description":"Update name, description, colour or icon. Renaming does not change the `slug`; `position` and `is_default` are not editable here. Requires a token created by an organization admin (the token acts as its creator; a non-admin token gets 403).","tags":["Teams"],"parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Renaming does NOT change the `slug`.","type":"string","minLength":1,"maxLength":100},"description":{"description":"Set to null to clear.","nullable":true,"type":"string","maxLength":500},"color":{"description":"Set to null to clear.","nullable":true,"type":"string","maxLength":50},"icon":{"description":"Set to null to clear.","nullable":true,"type":"string","maxLength":50}},"description":"Cross-field rule: at least one field must be provided — an empty object is rejected. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"}}}},"responses":{"200":{"description":"The updated team","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Team"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteTeam","summary":"Delete a team","description":"Deletes the team and moves everything it owned — every conversation and every channel (mailboxes, Meta pages, WhatsApp numbers, chat widgets) — to `destination_team_id`. Team chats are deleted; memberships are removed. **This endpoint takes a JSON request body** (unusual for DELETE): the destination is required, because defaulting it would silently re-route a customer mailbox. 400 when the team is the default team, when `destination_team_id` is the team itself, or when it is not a team in this organization. Requires a token created by an organization admin (the token acts as its creator; a non-admin token gets 403).","tags":["Teams"],"parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"destination_team_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Team that receives every conversation and channel currently on the deleted team. Must be a different team in the same organization."}},"required":["destination_team_id"]},"examples":{"move":{"summary":"Fold the team into another one","value":{"destination_team_id":"0d3a7f5e-1c2b-4a6d-9e8f-123456789abc"}}}}}},"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/teams/{teamId}/members":{"get":{"operationId":"listTeamMembers","summary":"List team members","description":"Returns the team's members with their team role, ordered by name. No pagination — bounded by the organization's seats.","tags":["Teams"],"parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"All members of the team","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"addTeamMember","summary":"Add a team member","description":"Adds an active member of the organization to the team. `role` defaults to `active`. 400 when `user_id` is not an active member of the organization (deactivated members must be reactivated first); 409 when the user is already on the team. Requires a token created by an organization admin (the token acts as its creator; a non-admin token gets 403).","tags":["Teams"],"parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"An active member of the organization (see `GET /users`)."},"role":{"default":"active","type":"string","enum":["active","observer"],"description":"`active` members receive assignments and inbox notifications for the team; `observer` members can read the team inbox but are never auto-assigned."}},"required":["user_id"]},"examples":{"add":{"summary":"Add an observer","value":{"user_id":"7b1e6c3a-5d4f-4e2b-8a9c-0f1e2d3c4b5a","role":"observer"}}}}}},"responses":{"201":{"description":"The new membership","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TeamMember"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"409":{"description":"The user is already a member of this team","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/teams/{teamId}/members/{userId}":{"patch":{"operationId":"updateTeamMember","summary":"Change a member's team role","description":"Switches the member between `active` and `observer`. 404 when the user is not a member of the team. Requires a token created by an organization admin (the token acts as its creator; a non-admin token gets 403).","tags":["Teams"],"parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["active","observer"],"description":"`active` members receive assignments and inbox notifications for the team; `observer` members can read the team inbox but are never auto-assigned."}},"required":["role"]}}}},"responses":{"200":{"description":"The updated membership","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TeamMember"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"removeTeamMember","summary":"Remove a team member","description":"Removes the user from the team. Their organization membership and any conversations assigned to them are untouched. 404 when the user is not a member of the team. The returned `id` is the user id. Requires a token created by an organization admin (the token acts as its creator; a non-admin token gets 403).","tags":["Teams"],"parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Removal confirmation (`id` is the user id)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/channels":{"get":{"operationId":"listChannels","summary":"List channels","description":"Returns every inbound channel the token can see, in the dashboard's sidebar order (email, then Messenger / Instagram / WhatsApp, then the widget and the phone line). No pagination — channels are few and capped by plan. Visibility follows the dashboard: an admin-owned token lists every channel; any other member's token lists the channels routed to teams that member belongs to. Members' personal mailboxes are never listed — they are not team channels. Match a channel to conversations with `GET /conversations?channel={type}&channel_id={id}`.","tags":["Channels"],"responses":{"200":{"description":"All visible channels","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Channel"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/tags":{"get":{"operationId":"listTags","summary":"List tags","description":"Every tag in the organization, newest first, with `conversation_count` (conversations currently carrying the tag) and `last_used_at` (when it was last added to one). Filter by name substring with `q`.","tags":["Tags"],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"q","in":"query","schema":{"type":"string","minLength":1,"maxLength":100},"description":"Case-insensitive substring filter on the tag name."}],"responses":{"200":{"description":"A page of tags","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createTag","summary":"Create a tag","description":"Creates a tag by name. Idempotent: tag identity is case-insensitive per organization, so posting a name that already exists (in any casing) returns the existing tag with status 200 and its stored spelling, instead of a duplicate; a new tag returns 201. `color`/`emoji`, when given, are applied either way. Conversations also create tags on first use when you set an unknown name in their `tags` — this endpoint is for creating a tag ahead of time or styling it.","tags":["Tags"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[^,]+$","description":"Tag name. Identity is case-insensitive per organization: creating \"vip\" when \"VIP\" exists returns the existing tag. No commas."},"color":{"description":"Preset palette color. Omit for neutral gray.","type":"string","enum":["gray","red","orange","amber","green","teal","blue","indigo","purple","pink"]},"emoji":{"description":"Native emoji rendered before the name (a ZWJ sequence is fine).","type":"string","minLength":1,"maxLength":16}},"required":["name"]},"examples":{"create":{"summary":"A styled tag","value":{"name":"Endurgreiðsla","color":"amber","emoji":"💸"}}}}}},"responses":{"200":{"description":"The tag already existed (returned unchanged apart from any color/emoji given)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Tag"}}}}}},"201":{"description":"The created tag","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Tag"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/tags/{tagId}":{"get":{"operationId":"getTag","summary":"Get a tag","tags":["Tags"],"parameters":[{"name":"tagId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The tag","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Tag"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateTag","summary":"Update a tag","description":"Rename a tag and/or change its appearance. A plain rename keeps the id: every conversation, automation rule and pinned list follows automatically. Renaming onto the name of an EXISTING tag (any casing) MERGES this tag into it: its conversations, automation-rule actions and pinned lists are repointed to the existing tag, this tag is deleted, and the response is the surviving tag (a different `id`, with its own stored spelling). `color`/`emoji` in the same request land on the surviving tag. The response is the tag row as it is after the change.","tags":["Tags"],"parameters":[{"name":"tagId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New name. Renaming onto the name of an EXISTING tag (any casing) MERGES this tag into it: conversations, automation rules and pinned lists are repointed, this tag is deleted, and the surviving tag is returned. No commas.","type":"string","minLength":1,"maxLength":100,"pattern":"^[^,]+$"},"color":{"description":"Preset palette color. Set to null to clear (neutral gray).","nullable":true,"type":"string","enum":["gray","red","orange","amber","green","teal","blue","indigo","purple","pink"]},"emoji":{"description":"Set to null to clear.","nullable":true,"type":"string","minLength":1,"maxLength":16}},"description":"Cross-field rule: at least one field must be provided — an empty object is rejected. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"}}}},"responses":{"200":{"description":"The tag after the change (the surviving tag, on a merge)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Tag"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteTag","summary":"Delete a tag","description":"Removes the tag from every conversation it is on, from every automation rule's add-tag/remove-tag actions (an action left without tags is dropped from the rule) and from every member's pinned-tag list, then deletes the tag. Not reversible.","tags":["Tags"],"parameters":[{"name":"tagId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/canned-responses":{"get":{"operationId":"listCannedResponses","summary":"List canned responses","description":"Newest first. An admin-owned token sees every response; another member's token sees responses with no team restriction plus those scoped to a team the member belongs to. `team_id` narrows the list to what the composer offers on a conversation of that team (unrestricted responses plus those scoped to it). The dashboard picker sorts by `category`, then `sort_order`, then `title` — apply that ordering client-side.","tags":["Canned responses"],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"team_id","in":"query","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"description":"Only responses available to this team: those with no team restriction plus those scoped to the team."},{"name":"category","in":"query","schema":{"type":"string","minLength":1,"maxLength":100},"description":"Exact category match."}],"responses":{"200":{"description":"A page of canned responses","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CannedResponse"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createCannedResponse","summary":"Create a canned response","description":"Creates a reply template. Every `team_ids` entry must be a team of the organization (400 otherwise). Plans cap the number of canned responses: at the cap the request fails with 402 `plan_limit`. Recorded in the organization's activity log with the token owner as actor.","tags":["Canned responses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255,"description":"Shown in the composer picker."},"content":{"type":"string","minLength":1,"maxLength":100000,"description":"The reply text inserted into the composer."},"category":{"description":"Free-text grouping label for the picker. Omit or null for uncategorized.","nullable":true,"type":"string","maxLength":100},"team_ids":{"description":"Teams the response is restricted to. Every id must be a team of the organization (400 otherwise). Empty = available to everyone. Duplicates are removed rather than rejected.","maxItems":100,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"sort_order":{"default":0,"description":"Position within its category in the picker (ascending).","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"exclude_from_ai":{"default":false,"description":"When true, AI reply drafts never cite or reuse this response. Agents can still insert it from the composer. Use it for internal templates (e.g. a request the team sends to a partner) that must not become a customer-facing draft.","type":"boolean"}},"required":["title","content"]},"examples":{"create":{"summary":"A refund template restricted to one team","value":{"title":"Endurgreiðsla staðfest","content":"Sæl/l,\n\nEndurgreiðslan hefur verið afgreidd og ætti að berast innan 3-5 virkra daga.\n\nKveðja,\nÞjónustuver","category":"Endurgreiðslur","team_ids":["3d2f4b2a-9c1e-4c7b-8f3e-1a2b3c4d5e6f"],"sort_order":10}}}}}},"responses":{"201":{"description":"The created canned response","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/CannedResponse"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"402":{"description":"Plan limit reached — `details` carries `{ feature, limit, current, plan }`","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/canned-responses/{responseId}":{"get":{"operationId":"getCannedResponse","summary":"Get a canned response","description":"Any member's token may fetch any response of the organization by id, regardless of team restriction.","tags":["Canned responses"],"parameters":[{"name":"responseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The canned response","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/CannedResponse"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateCannedResponse","summary":"Update a canned response","description":"Partial update; omitted fields are left as they are. `team_ids` replaces the whole list (every id must be a team of the organization; `[]` makes the response available to everyone). `exclude_from_ai: true` hides the response from AI reply drafts only; agents still see it in the composer. Recorded in the activity log.","tags":["Canned responses"],"parameters":[{"name":"responseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255},"content":{"type":"string","minLength":1,"maxLength":100000},"category":{"description":"Set to null to clear.","nullable":true,"type":"string","maxLength":100},"team_ids":{"description":"Teams the response is restricted to. Every id must be a team of the organization (400 otherwise). Empty = available to everyone. Duplicates are removed rather than rejected.","maxItems":100,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"sort_order":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"exclude_from_ai":{"description":"When true, AI reply drafts never cite or reuse this response. Agents can still insert it from the composer. Use it for internal templates (e.g. a request the team sends to a partner) that must not become a customer-facing draft.","type":"boolean"}},"description":"Cross-field rule: at least one field must be provided — an empty object is rejected. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"}}}},"responses":{"200":{"description":"The updated canned response","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/CannedResponse"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteCannedResponse","summary":"Delete a canned response","description":"Removes the template. Replies already sent with it are unaffected. Recorded in the activity log.","tags":["Canned responses"],"parameters":[{"name":"responseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/automation-rules":{"get":{"operationId":"listAutomationRules","summary":"List automation rules","description":"Returns every rule in the organization — active and inactive — in evaluation order (`priority` ascending, newest first on ties). No pagination: the set is bounded by the plan cap. Tag actions come back with tag names.","tags":["Automation rules"],"responses":{"200":{"description":"All automation rules","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AutomationRule"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createAutomationRule","summary":"Create an automation rule","description":"Creates a rule. The new rule takes effect on the next inbound conversation; it is **not** applied to existing conversations — call `POST /automation-rules/{ruleId}/apply` for that. `assign_to.userIds` / `assign_to_team.userIds` must be active members of the organization and `assign_to_team.teamId` one of its teams (400 otherwise). Tag names in `add_tag` / `remove_tag` are created on first use. The plan cap counts **active** rules: creating with `is_active: true` returns 402 when the cap is reached, creating inactive never does. Writes an `automation_rule_created` entry to the activity log.","tags":["Automation rules"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Rule name, shown in the dashboard and the activity log."},"description":{"nullable":true,"description":"Free-text note about what the rule is for. Set to null to clear.","type":"string"},"is_active":{"default":true,"description":"Inactive rules are stored but never evaluated. Only ACTIVE rules count toward the plan cap.","type":"boolean"},"condition_logic":{"default":"all","description":"`all` — every condition must match (AND); `any` — at least one (OR); `none` — none may match (NOR).","type":"string","enum":["all","any","none"]},"conditions":{"minItems":1,"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["from_email","from_domain","subject","body","channel","team","to_email","to_domain","cc_email","any_recipient","has_attachment","attachment_name","outside_opening_hours"]},"operator":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","ends_with","matches"]},"value":{"type":"string","minLength":1}},"required":["field","operator","value"],"description":"One condition. `field` names what is inspected on the inbound message (`from_email`, `from_domain`, `subject`, `body`, `channel`, `team`, `to_email`, `to_domain`, `cc_email`, `any_recipient`, `has_attachment`, `attachment_name`, `outside_opening_hours`); `operator` is how `value` is compared (`matches` is a regular expression). `has_attachment` and `outside_opening_hours` are booleans: `value` is `\"true\"` or `\"false\"` with operator `is` / `is_not`; `outside_opening_hours` refers to the organization opening hours and is never true for an organization without them."},"description":"At least one condition. Combined per `condition_logic`."},"actions":{"minItems":1,"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["ignore"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_merge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_ignore"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["forward_email"]},"toEmail":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"includeAttachments":{"type":"boolean"}},"required":["type","toEmail"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_status"]},"status":{"type":"string","enum":["open","pending","resolved","ignored"]}},"required":["type","status"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to"]},"userIds":{"minItems":1,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]}},"required":["type","userIds"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to_team"]},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"roundRobin":{"type":"boolean"},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]},"userIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"onlyOnline":{"type":"boolean"}},"required":["type","teamId"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_contact_from_body"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["stop_processing_more_rules"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["snooze_ticket"]},"minutes":{"type":"integer","minimum":1,"maximum":525600}},"required":["type","minutes"]},{"type":"object","properties":{"type":{"type":"string","enum":["archive"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["auto_reply"]},"message":{"type":"string","minLength":1,"maxLength":10000}},"required":["type","message"]},{"type":"object","properties":{"type":{"type":"string","enum":["add_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]},{"type":"object","properties":{"type":{"type":"string","enum":["remove_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]}],"description":"One action, discriminated on `type`. Keys inside an action are camelCase (`toEmail`, `includeAttachments`, `userIds`, `teamId`, `roundRobin`, `onlyOnline`) — the one deliberate exception to snake_case on this API, because the action JSON is the rule engine's own format and is meant to round-trip verbatim through version control. `add_tag`/`remove_tag` carry tag names. `assign_to.userIds` and `assign_to_team.userIds` must be active members of the organization; `assign_to_team.teamId` must be one of the organization's teams."},"description":"At least one action, applied in order when the conditions match."},"priority":{"default":0,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Evaluation order across the organization's rules — lower runs first. Ties break newest-first."}},"required":["name","conditions","actions"]},"examples":{"create":{"summary":"Route accounting mail to a team and tag it","value":{"name":"Route invoices to Finance","description":"Anything from the accounting system goes to the finance team and gets tagged.","is_active":true,"condition_logic":"any","conditions":[{"field":"from_domain","operator":"is","value":"dk.is"},{"field":"subject","operator":"contains","value":"Reikningur"}],"actions":[{"type":"assign_to_team","teamId":"9c2f6c1e-7d5c-4b3a-9a4e-3f0b1d2c5e77","strategy":"round_robin"},{"type":"add_tag","tags":["Reikningar"]}],"priority":10}}}}}},"responses":{"201":{"description":"The created rule","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AutomationRule"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"402":{"description":"Plan limit reached — `details` carries `{ feature, limit, current, plan }`","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/automation-rules/{ruleId}":{"get":{"operationId":"getAutomationRule","summary":"Get an automation rule","tags":["Automation rules"],"parameters":[{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The rule","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AutomationRule"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateAutomationRule","summary":"Update an automation rule","description":"Partial update. `conditions` and `actions` are replaced as whole lists when sent — send the complete list, not a diff (which is what makes re-applying a rule from version control a single call). The same reference checks as on create apply to `actions`. Setting `is_active: true` on an inactive rule counts against the plan cap for active rules (402 when reached). Writes an `automation_rule_updated` entry to the activity log listing the fields that changed.","tags":["Automation rules"],"parameters":[{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Rule name, shown in the dashboard and the activity log."},"description":{"nullable":true,"description":"Free-text note about what the rule is for. Set to null to clear.","type":"string"},"is_active":{"description":"Activating an inactive rule counts as creating one for the plan cap (402 when the cap is reached).","type":"boolean"},"condition_logic":{"description":"`all` — every condition must match (AND); `any` — at least one (OR); `none` — none may match (NOR).","type":"string","enum":["all","any","none"]},"conditions":{"description":"Replaces the whole list — send every condition, not a diff.","minItems":1,"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["from_email","from_domain","subject","body","channel","team","to_email","to_domain","cc_email","any_recipient","has_attachment","attachment_name","outside_opening_hours"]},"operator":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","ends_with","matches"]},"value":{"type":"string","minLength":1}},"required":["field","operator","value"],"description":"One condition. `field` names what is inspected on the inbound message (`from_email`, `from_domain`, `subject`, `body`, `channel`, `team`, `to_email`, `to_domain`, `cc_email`, `any_recipient`, `has_attachment`, `attachment_name`, `outside_opening_hours`); `operator` is how `value` is compared (`matches` is a regular expression). `has_attachment` and `outside_opening_hours` are booleans: `value` is `\"true\"` or `\"false\"` with operator `is` / `is_not`; `outside_opening_hours` refers to the organization opening hours and is never true for an organization without them."}},"actions":{"description":"Replaces the whole list — send every action, not a diff.","minItems":1,"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["ignore"]},"reason":{"type":"string"}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_merge"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["never_ignore"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["forward_email"]},"toEmail":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"includeAttachments":{"type":"boolean"}},"required":["type","toEmail"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_status"]},"status":{"type":"string","enum":["open","pending","resolved","ignored"]}},"required":["type","status"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to"]},"userIds":{"minItems":1,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]}},"required":["type","userIds"]},{"type":"object","properties":{"type":{"type":"string","enum":["assign_to_team"]},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"roundRobin":{"type":"boolean"},"strategy":{"type":"string","enum":["round_robin","least_busy","random","all_at_once"]},"userIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"onlyOnline":{"type":"boolean"}},"required":["type","teamId"]},{"type":"object","properties":{"type":{"type":"string","enum":["set_contact_from_body"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["stop_processing_more_rules"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["snooze_ticket"]},"minutes":{"type":"integer","minimum":1,"maximum":525600}},"required":["type","minutes"]},{"type":"object","properties":{"type":{"type":"string","enum":["archive"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["auto_reply"]},"message":{"type":"string","minLength":1,"maxLength":10000}},"required":["type","message"]},{"type":"object","properties":{"type":{"type":"string","enum":["add_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]},{"type":"object","properties":{"type":{"type":"string","enum":["remove_tag"]},"tags":{"minItems":1,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tag names (not ids). A name that does not exist yet is created on first use. Matching is case-insensitive."}},"required":["type","tags"]}],"description":"One action, discriminated on `type`. Keys inside an action are camelCase (`toEmail`, `includeAttachments`, `userIds`, `teamId`, `roundRobin`, `onlyOnline`) — the one deliberate exception to snake_case on this API, because the action JSON is the rule engine's own format and is meant to round-trip verbatim through version control. `add_tag`/`remove_tag` carry tag names. `assign_to.userIds` and `assign_to_team.userIds` must be active members of the organization; `assign_to_team.teamId` must be one of the organization's teams."}},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Evaluation order across the organization's rules — lower runs first. Ties break newest-first."}},"description":"Cross-field rule: at least one field must be provided — an empty object is rejected. `conditions` and `actions` are replaced as whole lists, never merged. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"}}}},"responses":{"200":{"description":"The updated rule","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AutomationRule"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"402":{"description":"Plan limit reached — `details` carries `{ feature, limit, current, plan }`","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteAutomationRule","summary":"Delete an automation rule","description":"Removes the rule permanently. Conversations it already routed are untouched. Writes an `automation_rule_deleted` entry to the activity log.","tags":["Automation rules"],"parameters":[{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/automation-rules/{ruleId}/apply":{"post":{"operationId":"applyAutomationRule","summary":"Apply a rule to existing conversations","description":"Runs the saved rule once against the organization's existing **open and pending** conversations (resolved and ignored ones are never touched) and applies its actions to every match. Each conversation is evaluated on its first inbound message; conversations without one (created outbound) are skipped. Actions that only make sense at intake — `forward_email`, `never_merge`, `never_ignore`, `set_contact_from_body`, `stop_processing_more_rules` — are ignored during a retroactive run. At most 500 conversations (oldest first) are scanned per call; `capped: true` tells you there were more. Runs synchronously and works on inactive rules too. No request body. Writes an `automation_rule_applied_retroactively` entry to the activity log with the same tally.","tags":["Automation rules"],"parameters":[{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"What the run did","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AutomationRuleApplyResult"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/knowledge-sources":{"get":{"operationId":"listKnowledgeSources","summary":"List knowledge sources","description":"Newest first, keyset-paginated. Items omit `content` (a source can hold 100 KB of text) — fetch one source to read it. Filter by `status` to find sources that failed (`error`) or are still embedding (`processing`).","tags":["Knowledge"],"parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from a previous response's `pagination.next_cursor`. Omit for the first page."},{"name":"limit","in":"query","schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"description":"Items per page."},{"name":"status","in":"query","schema":{"type":"string","enum":["processing","ready","error"]},"description":"Only sources in this processing state."},{"name":"source_type","in":"query","schema":{"type":"string","enum":["manual","url","file","migrated_init_doc","google_doc","google_sheet"]},"description":"Only sources of this type."}],"responses":{"200":{"description":"A page of knowledge sources","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSource"}},"pagination":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"post":{"operationId":"createKnowledgeSource","summary":"Create a knowledge source","description":"Adds a text source (`kind: \"text\"`) or a URL source (`kind: \"url\"`). The source is returned immediately with `status: processing`; chunking and embedding run in the background — poll `GET /knowledge-sources/{id}` until `status` is `ready` or `error`. A URL source starts with empty `content`, filled once the page is fetched; a Google Docs/Sheets link becomes a live `google_doc`/`google_sheet` source that Elvo keeps in sync (400 when Google import is not configured on this deployment). Non-public URLs (private networks, non-http schemes) are rejected with 400. Requires a token created by an organization admin (403 otherwise).","tags":["Knowledge"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["text"],"description":"A text source: you supply the content directly."},"title":{"type":"string","minLength":1,"maxLength":500,"description":"Display title of the source."},"content":{"type":"string","minLength":1,"maxLength":100000,"description":"Plain text or Markdown the AI should learn from. Max 100 000 characters."}},"required":["kind","title","content"]},{"type":"object","properties":{"kind":{"type":"string","enum":["url"],"description":"A URL source: Elvo fetches the page (or Google Doc/Sheet) and extracts its text."},"url":{"type":"string","maxLength":2000,"format":"uri","description":"Public http(s) URL. Private-network and non-http(s) URLs are rejected with 400. A Google Docs/Sheets link becomes a live source that Elvo keeps in sync."},"title":{"description":"Display title. Defaults to the URL when omitted.","type":"string","minLength":1,"maxLength":500}},"required":["kind","url"]}],"description":"Discriminated on `kind`: `text` takes `title` + `content`; `url` takes `url` and an optional `title`."},"examples":{"text":{"summary":"A text source owned by your pipeline","value":{"kind":"text","title":"Skilaskilmálar","content":"# Skilaskilmálar\n\nVörum má skila innan 30 daga frá kaupum gegn framvísun kvittunar."}},"url":{"summary":"Import a public web page","value":{"kind":"url","url":"https://example.is/skilmalar","title":"Skilmálar (vefur)"}}}}}},"responses":{"201":{"description":"The created source, including `content` (`status: processing`)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/KnowledgeSourceWithContent"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/knowledge-sources/{sourceId}":{"get":{"operationId":"getKnowledgeSource","summary":"Get a knowledge source","description":"The full source including `content` — the stored text for text sources, the last extracted text for url/file/Google sources (null or empty until first processed).","tags":["Knowledge"],"parameters":[{"name":"sourceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The knowledge source","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/KnowledgeSourceWithContent"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"patch":{"operationId":"updateKnowledgeSource","summary":"Update a knowledge source","description":"Change `title` and/or `content`. `content` is only accepted on `manual` and `migrated_init_doc` sources (400 on url/file/Google sources, whose text Elvo re-extracts). A real change resets the source to `status: processing` and re-embeds it asynchronously — `status` flips to `ready` or `error` when the job finishes. A patch whose values equal the stored ones is a no-op: 200 with the unchanged source, nothing re-embedded. Requires a token created by an organization admin (403 otherwise).","tags":["Knowledge"],"parameters":[{"name":"sourceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"description":"New display title.","type":"string","minLength":1,"maxLength":500},"content":{"description":"Replacement text. Only accepted on `manual` and `migrated_init_doc` sources (400 otherwise): url, file and Google sources are re-extracted by Elvo and would overwrite it on the next sync.","type":"string","minLength":1,"maxLength":100000}},"description":"Cross-field rule: at least one field must be provided — an empty object is rejected. (Cross-field rules are `.refine()` checks, which Zod cannot express in JSON Schema, so they are stated here rather than in the field constraints below.)"}}}},"responses":{"200":{"description":"The updated source (`status: processing` when re-embedding was queued)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/KnowledgeSourceWithContent"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"deleteKnowledgeSource","summary":"Delete a knowledge source","description":"Removes the source and its embedded chunks; the AI stops answering from it immediately. For `file` sources the uploaded file is deleted too. Requires a token created by an organization admin (403 otherwise).","tags":["Knowledge"],"parameters":[{"name":"sourceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/knowledge-sources/files":{"post":{"operationId":"uploadKnowledgeSourceFile","summary":"Upload a file as a knowledge source","description":"The multipart twin of `POST /knowledge-sources`: send `multipart/form-data` with a `file` part (pdf, txt, md, docx, xlsx, xls, csv; at most 4 MB — requests above the platform's ~4.5 MB body limit are rejected with a plain 413 before reaching the API) and an optional `title`. Returns the new `file` source with `status: processing` and `content: null`; Elvo extracts the text and embeds it in the background. To update the document later, use `PUT /knowledge-sources/{id}/file` rather than uploading a second source. Requires a token created by an organization admin (403 otherwise).","tags":["Knowledge"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The document: pdf, txt, md, docx, xlsx, xls or csv, at most 4 MB."},"title":{"type":"string","maxLength":500,"description":"Display title. Defaults to the file name when omitted."}}}}}},"responses":{"201":{"description":"The created file source (`status: processing`)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/KnowledgeSourceWithContent"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/knowledge-sources/{sourceId}/file":{"put":{"operationId":"replaceKnowledgeSourceFile","summary":"Replace the file behind a knowledge source","description":"Swaps the document of an existing `file` source, keeping its id and title — for a scheduled job that re-uploads the current version of a document, or an agent replacing an outdated one. Same `multipart/form-data` `file` part and limits as the upload endpoint (a `title` part is ignored; change titles with PATCH). The source is returned with `status: processing`; if the new file extracts to the same text as before, re-embedding is skipped and it returns to `ready` cheaply. The previous file is deleted. 400 when the source is not a `file` source. Requires a token created by an organization admin (403 otherwise).","tags":["Knowledge"],"parameters":[{"name":"sourceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The document: pdf, txt, md, docx, xlsx, xls or csv, at most 4 MB."}}}}}},"responses":{"200":{"description":"The source, now pointing at the new file (`status: processing`)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/KnowledgeSourceWithContent"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/knowledge-sources/{sourceId}/sync":{"post":{"operationId":"syncKnowledgeSource","summary":"Re-sync a live knowledge source","description":"Re-fetches a `url`, `google_doc` or `google_sheet` source now instead of waiting for the scheduled refresh (400 for other source types). Returns the source with `status: processing`; if the extracted text is unchanged the job skips re-embedding and the source returns to `ready`. When a sync is already running, nothing new is queued and the current source (still `processing`) is returned. Requires a token created by an organization admin (403 otherwise).","tags":["Knowledge"],"parameters":[{"name":"sourceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The source, now (or still) `status: processing`","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/KnowledgeSourceWithContent"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/reports/performance":{"get":{"operationId":"getPerformanceReport","summary":"Get the performance report","description":"The Frammistaða dashboard as JSON: headline metrics (`summary`), time series and breakdowns (`trends`), the resolved reporting `window`, and the `config` the metrics were computed with. Latencies are minutes, rates are percentages (0–100), both rounded to one decimal. `tfr` is time to first reply, `fcr` first-contact resolution, `touchless` machine-handled closes, `ai_hours_saved` accepted AI draft replies × minutes per reply ÷ 60. `is_real_data: false` means the organization has too little reply history and only `generated_at` is returned. Metrics are org-wide aggregates for the token's organization; `team_id` and `agent_ids` narrow them (and drop the peer-benchmark fields, which are only comparable org-wide on rolling windows). Payloads are cached for 5 minutes per scope and the cache cannot be bypassed from the API — poll no more often than that. Requires the dashboard's access rule: admin-owned token, or any member's token when the organization opened Frammistaða to all members (403 otherwise).","tags":["Reports"],"parameters":[{"name":"period","in":"query","schema":{"default":"last_30_days","type":"string","enum":["this_week","last_week","last_7_days","last_30_days","last_90_days","all_time","custom"]},"description":"Reporting window. `this_week` is the current calendar week (Monday 00:00 UTC to now) and `last_week` the previous whole Monday–Sunday week, each compared against the same span one week earlier; rolling windows end at the current time; `all_time` covers the organization's whole history; `custom` uses `from`/`to`."},{"name":"from","in":"query","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"description":"Inclusive first day (YYYY-MM-DD, UTC). Required when `period=custom`, ignored otherwise."},{"name":"to","in":"query","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"description":"Inclusive last day (YYYY-MM-DD, UTC). Required when `period=custom`, ignored otherwise. A custom range spans at most 730 days."},{"name":"team_id","in":"query","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"description":"Restrict the report to conversations owned by one team. Peer benchmarks are omitted when set."},{"name":"agent_ids","in":"query","schema":{"type":"string"},"description":"Comma-separated user ids. Restricts the report to conversations these agents replied to. Peer benchmarks are omitted when set."},{"name":"comparison","in":"query","schema":{"type":"string","enum":["previous_period","previous_year"]},"description":"Which window the `previous` values compare against: the equal-length window immediately before (default), or the same calendar dates one year earlier. `previous_year` is ignored for `period=all_time`."}],"responses":{"200":{"description":"The performance report for the requested scope","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/PerformanceReport"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"The token's member may not view the performance report (not an admin, and the organization has not opened Frammistaða to all members)","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/me":{"get":{"operationId":"getMe","summary":"Get the token owner, organization and token","description":"Who this token acts as: the user (with their `role` in the organization), the organization every request is scoped to, and the token itself (`scope` `read` or `write`, `expires_at`). Call it first to learn your own user id, e.g. to filter `GET /conversations?assigned_to_id=`.","tags":["Me"],"responses":{"200":{"description":"The token owner, organization and token","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Me"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}},"/me/out-of-office":{"get":{"operationId":"getOutOfOffice","summary":"Get my out-of-office status","description":"The stored status, whether or not its window is in effect right now (`active` says which), or `null` when none is set. A status whose `ends_at` has passed is kept but inactive until it is replaced or cleared.","tags":["Me"],"responses":{"200":{"description":"The status, or null","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/OutOfOffice"},{"type":"object","nullable":true}],"description":"`null` when the member has never set a status (or cleared it)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"put":{"operationId":"setOutOfOffice","summary":"Set my out-of-office status","description":"Replaces the whole status. While the window is in effect and `channels.chat` is on, teammates see a purple presence dot and a banner above the composer in internal chats naming `delegate_user_id`; with `channels.email` on, every inbound message to one of your personal connected mailboxes gets `message` back once per sender per away period, sent from that mailbox. Re-saving with the same `starts_at`/`ends_at` keeps the current away period (senders already answered are not answered again); changing the window starts a new one. Rules, each a 400: at least one channel; `ends_at` after `starts_at` and in the future; the delegate must be an active member of one of your organizations and not you.","tags":["Me"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"default":null,"description":"ISO 8601. Omit or `null` to start now.","nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"ends_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"ISO 8601, exclusive. Must be in the future and after `starts_at`."},"message":{"type":"string","maxLength":1000,"description":"Plain text. Shown to teammates in chat and sent as the email auto-reply."},"delegate_user_id":{"default":null,"description":"The colleague teammates and senders are pointed to. Must be an active member of one of your organizations and not yourself.","nullable":true,"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channels":{"type":"object","properties":{"chat":{"type":"boolean","description":"Purple presence dot and a banner above the composer for teammates about to message you in internal chats."},"email":{"type":"boolean","description":"One auto-reply per sender, per away period, sent from each of your personal connected mailboxes."}},"required":["chat","email"]}},"required":["ends_at","message","channels"],"description":"Rules (each a 400): at least one of `channels.chat` / `channels.email` must be true; `ends_at` must be after `starts_at` and in the future; `delegate_user_id` must be an active member of an organization you belong to and cannot be your own id."},"examples":{"leave":{"summary":"A week off, Aron covers chat and email","value":{"starts_at":null,"ends_at":"2026-10-06T00:00:00+00:00","message":"Ég er í fríi til 6. október. Aron svarar á meðan, aron@example.is.","delegate_user_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","channels":{"chat":true,"email":true}}},"chatOnly":{"summary":"Chat status only, scheduled in advance","value":{"starts_at":"2026-12-22T00:00:00+00:00","ends_at":"2027-01-04T00:00:00+00:00","message":"Jólafrí. Hafðu samband við þjónustuverið.","delegate_user_id":null,"channels":{"chat":true,"email":false}}}}}}},"responses":{"200":{"description":"The stored status","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/OutOfOffice"},{"type":"object","nullable":true}],"description":"`null` when the member has never set a status (or cleared it)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}},"delete":{"operationId":"clearOutOfOffice","summary":"Clear my out-of-office status","description":"Removes the status: the dot and banner disappear for teammates and no further email auto-replies are sent. `id` is your own user id. Setting a new status afterwards starts a fresh away period.","tags":["Me"],"responses":{"200":{"description":"Cleared","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"403":{"description":"Token lacks permission for this action","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["authentication_error","authorization_error","validation_error","not_found","method_not_allowed","rate_limit_exceeded","conflict","plan_limit","internal_error"]},"message":{"type":"string"},"details":{}}}}}}}}}}}}}