{"openapi":"3.0.0","paths":{"/api/auth/sign-in":{"post":{"description":"Returns a short-lived JWT (15 minutes) and a refresh token (7 days). Use the JWT as `Authorization: Bearer <jwt>` to create an Access Token (`POST /api/companies/{companyId}/access-tokens`); integrations then authenticate with the Access Token only. Rate limited to 10 attempts per minute.","operationId":"signIn","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInRequestDto"}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthTokensResponseDto"}}}},"400":{"description":"Invalid email or password"},"401":{"description":"Invalid credentials"}},"summary":"Sign in with email and password","tags":["Authentication"],"security":[]}},"/api/auth/refresh":{"post":{"description":"Exchanges a refresh token for a new JWT and a new refresh token. Call it when the 15-minute JWT expires instead of signing in again. Integrations that use an Access Token don't need this.","operationId":"refreshToken","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"refreshToken":{"type":"string","description":"Refresh token from the sign-in or a previous refresh response","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}},"required":["refreshToken"]}}}},"responses":{"200":{"description":"New token pair","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthTokensResponseDto"}}}},"400":{"description":"Invalid or expired refresh token"},"401":{"description":"Unauthorized - Invalid refresh token"}},"summary":"Refresh the JWT","tags":["Authentication"],"security":[]}},"/api/auth/me":{"get":{"description":"Returns the profile of the user the JWT belongs to. Use it to check that a JWT is valid before creating an Access Token.","operationId":"getCurrentUser","parameters":[],"responses":{"200":{"description":"Current user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentUserResponseDto"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token"}},"security":[{"jwt-auth":[]}],"summary":"Get the signed-in user","tags":["Authentication"]}},"/api/companies/{companyId}/access-tokens":{"get":{"description":"Returns the company's active (not revoked) access tokens. Token values are never returned here — only `tokenPrefix` to recognise them. Requires a JWT of the company owner or an admin.","operationId":"listAccessTokens","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company the tokens belong to","schema":{"format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}}],"responses":{"200":{"description":"Active access tokens","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AccessTokenResponseDto"}}}}},"403":{"description":"Only the company owner or an admin can manage access tokens"}},"security":[{"jwt-auth":[]}],"summary":"List access tokens","tags":["Access Tokens"]},"post":{"description":"Creates a long-lived API key for the company. The `token` value in the response is shown **only once** — store it securely and send it as the `X-Access-Token` header on API requests. The token reaches only this company's resources. Requires a JWT of the company owner or an admin.","operationId":"createAccessToken","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company the tokens belong to","schema":{"format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccessTokenDto"}}}},"responses":{"201":{"description":"Token created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedAccessTokenResponseDto"}}}},"403":{"description":"Only the company owner or an admin can manage access tokens"}},"security":[{"jwt-auth":[]}],"summary":"Create an access token","tags":["Access Tokens"]}},"/api/companies/{companyId}/access-tokens/{tokenId}":{"patch":{"description":"Changes the token's display name. The token value and its access stay the same. Requires a JWT of the company owner or an admin.","operationId":"updateAccessToken","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company the tokens belong to","schema":{"format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}},{"name":"tokenId","required":true,"in":"path","description":"Token id from the list or create response","schema":{"format":"uuid","example":"9b2f1c7e-4d3a-4e8b-a1f0-2c6d5e7f8a90","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAccessTokenDto"}}}},"responses":{"200":{"description":"Updated token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessTokenResponseDto"}}}},"403":{"description":"Only the company owner or an admin can manage access tokens"},"404":{"description":"Token not found in this company"}},"security":[{"jwt-auth":[]}],"summary":"Rename an access token","tags":["Access Tokens"]},"delete":{"description":"Deactivates the token immediately: requests sent with it are rejected with 401 from now on. Revocation cannot be undone — create a new token instead. Requires a JWT of the company owner or an admin.","operationId":"revokeAccessToken","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company the tokens belong to","schema":{"format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}},{"name":"tokenId","required":true,"in":"path","description":"Token id from the list or create response","schema":{"format":"uuid","example":"9b2f1c7e-4d3a-4e8b-a1f0-2c6d5e7f8a90","type":"string"}}],"responses":{"200":{"description":"Token revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokedAccessTokenResponseDto"}}}},"403":{"description":"Only the company owner or an admin can manage access tokens"},"404":{"description":"Token not found in this company"}},"security":[{"jwt-auth":[]}],"summary":"Revoke an access token","tags":["Access Tokens"]}},"/api/companies/my":{"get":{"description":"Returns the companies the signed-in user owns or is a member of. Use it after sign-in to find the `companyId` for creating an Access Token. With an Access Token it returns only the token's company.","operationId":"listMyCompanies","parameters":[],"responses":{"200":{"description":"Companies of the current user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyListResponseDto"}}}},"401":{"description":"Unauthorized - Missing or invalid JWT or access token"}},"security":[{"jwt-auth":[]}],"summary":"List my companies","tags":["Companies"]}},"/api/companies/{id}":{"get":{"description":"Returns the company with its members and subscriptions. An Access Token can read only its own company.","operationId":"getCompany","parameters":[{"name":"id","required":true,"in":"path","description":"Company id","schema":{"example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}}],"responses":{"200":{"description":"Company","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyResponseDto"}}}},"400":{"description":"Invalid company ID format"},"404":{"description":"Company not found"}},"security":[{"access-token":[]}],"summary":"Get a company","tags":["Companies"]}},"/api/assistants-new":{"get":{"description":"Returns a page of the company's assistants, most recently updated first. Paginate with `skip`/`take` (default page size 10), search by name with `q`. Draft variants are excluded unless `includeVariants=true`.","operationId":"listAssistants","parameters":[{"name":"companyId","required":false,"in":"query","description":"Company to list. Defaults to every company you can access (with an Access Token — its company).","schema":{"format":"uuid","type":"string"}},{"name":"folderId","required":false,"in":"query","description":"Filter by folder","schema":{"format":"uuid","type":"string"}},{"name":"q","required":false,"in":"query","description":"Case-insensitive search by name","schema":{"type":"string"}},{"name":"skip","required":false,"in":"query","description":"Number of items to skip","schema":{"default":0,"type":"number"}},{"name":"take","required":false,"in":"query","description":"Page size","schema":{"default":10,"type":"number"}},{"name":"includeVariants","required":false,"in":"query","description":"Include draft variants of assistants","schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"description":"Page of assistants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantNewListResponseDto"}}}},"403":{"description":"`companyId` is not a company you have access to"}},"security":[{"access-token":[]}],"summary":"List assistants","tags":["Assistants"]},"post":{"description":"Creates an assistant in your company. Built-in system tools are added automatically and the voice agent is provisioned in the background, so the assistant can answer chats right away and calls shortly after.","operationId":"createAssistant","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssistantNewDto"}}}},"responses":{"201":{"description":"Assistant created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantNewCreatedResponseDto"}}}},"400":{"description":"Validation failed, e.g. unknown text model id"},"403":{"description":"`companyId` is not the company of your token"}},"security":[{"access-token":[]}],"summary":"Create an assistant","tags":["Assistants"]}},"/api/assistants-new/{id}":{"get":{"description":"Returns the assistant's configuration: prompt, greeting, language and voice behaviour settings.","operationId":"getAssistant","parameters":[{"name":"id","required":true,"in":"path","description":"Assistant id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"The assistant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantNewResponseDto"}}}},"404":{"description":"Assistant not found or belongs to another company"}},"security":[{"access-token":[]}],"summary":"Get an assistant","tags":["Assistants"]},"patch":{"description":"Partial update: send only the fields to change. Changing voice settings (`vaRegistration`) re-syncs the voice agent in the background.","operationId":"updateAssistant","parameters":[{"name":"id","required":true,"in":"path","description":"Assistant id","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAssistantNewDto"}}}},"responses":{"200":{"description":"Updated assistant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantNewResponseDto"}}}},"400":{"description":"Validation failed or the selected provider can't be used"},"404":{"description":"Assistant not found or belongs to another company"}},"security":[{"access-token":[]}],"summary":"Update an assistant","tags":["Assistants"]},"delete":{"description":"Deletes the assistant with its tools and knowledge. If a voice agent was provisioned, it is torn down first: the response is `deleted: false, terminating: true` and the assistant disappears once teardown completes.","operationId":"deleteAssistant","parameters":[{"name":"id","required":true,"in":"path","description":"Assistant id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Deletion accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantNewDeleteResponseDto"}}}},"404":{"description":"Assistant not found or belongs to another company"}},"security":[{"access-token":[]}],"summary":"Delete an assistant","tags":["Assistants"]}},"/api/assistants-new/{id}/copy":{"post":{"description":"Creates a new assistant in the same company with the source's settings, tools and knowledge. Optionally switch the voice engine for the copy via `vaRegistration`.","operationId":"copyAssistant","parameters":[{"name":"id","required":true,"in":"path","description":"Assistant id","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopyAssistantNewDto"}}}},"responses":{"201":{"description":"The copy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantNewCreatedResponseDto"}}}},"400":{"description":"Copy failed"},"404":{"description":"Assistant not found or belongs to another company"}},"security":[{"access-token":[]}],"summary":"Copy an assistant","tags":["Assistants"]}},"/api/assistant-tools-new":{"get":{"description":"Returns the company's tools, newest first. Filter by `assistantId` to get one assistant's tools; `search` matches the tool name. Paginate with `skip` / `take`.","operationId":"listTools","parameters":[{"name":"assistantId","required":false,"in":"query","description":"Only tools of this assistant","schema":{"format":"uuid","type":"string"}},{"name":"search","required":false,"in":"query","description":"Case-insensitive match on the name","schema":{"type":"string"}},{"name":"skip","required":false,"in":"query","description":"Number of items to skip (pagination)","schema":{"example":0,"type":"number"}},{"name":"take","required":false,"in":"query","description":"Page size","schema":{"example":20,"type":"number"}}],"responses":{"200":{"description":"Page of tools","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantToolListResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"List tools","tags":["Assistant Tools"]},"post":{"description":"Adds a webhook tool to an assistant. The assistant sees `name`, `description` and `params` and decides on its own when to call the tool, so describe clearly what it does and when to use it.\n\nTool types (`type`):\n\n- `webhook_action` (default) — called by the assistant during a chat or a voice call when the conversation needs it.\n- `webhook_init` — voice: called once when a call starts; its response provides dynamic variables for the prompt.\n- `webhook_postcall` — voice: called after a call ends with the call result.\n- `webhook_client` — executed by the client application, no URL needed.\n\n### How Happ calls your endpoint\n\nWhen the assistant decides to use the tool, Happ sends an HTTP request to `apiCallUrl` with `httpMethod`:\n\n- **POST / PUT / PATCH** — the arguments (`params`) as a JSON body.\n- **GET / DELETE** — the arguments as query-string parameters.\n\nHeaders:\n\n| Header | Value |\n| --- | --- |\n| `Content-Type` | `application/json` |\n| `User-Agent` | `Happ-Platform-API/1.0` |\n| `X-Signature` | `sha256=<hex HMAC-SHA256 of the raw body, keyed by webhookSecret>` — only when a secret is set and the request has a body |\n| `Authorization` | `Basic <webhookSecret>` — only when a secret is set (legacy check; prefer `X-Signature`) |\n\nVerify `X-Signature` against the raw request body before parsing it. Respond with JSON — the assistant reads the body and uses it in its reply, so return short, self-explanatory fields (e.g. `{\"status\": \"shipped\", \"eta\": \"2026-10-09\"}`). On a non-2xx response or a network error the assistant is told the operation failed and does not retry.","operationId":"createTool","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssistantToolNewDto"}}}},"responses":{"201":{"description":"The created tool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantToolResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Create a tool","tags":["Assistant Tools"]}},"/api/assistant-tools-new/{id}":{"get":{"description":"Returns one tool. `webhookSecret` is write-only and never returned.","operationId":"getTool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The tool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantToolResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Get a tool","tags":["Assistant Tools"]},"patch":{"description":"Partially updates a tool: only the fields you send change. Send `disabled: true` to switch a tool off without deleting it. The platform post-call system tool can't be modified.","operationId":"updateTool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAssistantToolNewDto"}}}},"responses":{"200":{"description":"The updated tool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantToolResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Update a tool","tags":["Assistant Tools"]},"delete":{"description":"Removes the tool from the assistant. System tools (`isSystem: true`) can't be deleted.","operationId":"deleteTool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tool deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantToolDeletedResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Delete a tool","tags":["Assistant Tools"]}},"/api/assistant-tools-new/{id}/test":{"post":{"description":"Sends a REAL request to the tool's `apiCallUrl` with the `params` you pass — exactly as the assistant would, including the signature headers — and returns your endpoint's status, latency and body. Use it to debug your endpoint. Only tools with a URL and HTTP method can be tested.","operationId":"testTool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestToolDto"}}}},"responses":{"201":{"description":"Result of the call to your endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantToolTestResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Test a tool","tags":["Assistant Tools"]}},"/api/assistant-tools-new/{id}/logs":{"get":{"description":"History of the assistant calling this tool in conversations, newest first: arguments sent, your endpoint's response, success and latency, plus aggregate stats. Paginate with `skip` / `take` (default 20).","operationId":"listToolCalls","parameters":[{"name":"skip","required":false,"in":"query","description":"Number of items to skip (pagination)","schema":{"example":0,"type":"number"}},{"name":"take","required":false,"in":"query","description":"Page size","schema":{"example":20,"type":"number"}},{"name":"status","required":false,"in":"query","description":"Only calls with this outcome","schema":{"type":"string","enum":["success","error","pending"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Page of tool calls with stats","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantToolCallListResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"List tool calls","tags":["Assistant Tools"]}},"/api/assistant-knowledge-new":{"get":{"description":"Returns the company's knowledge items. Filter by `assistantId` to get one assistant's knowledge base; `search` matches the name. Paginate with `skip` / `take`.","operationId":"listKnowledge","parameters":[{"name":"assistantId","required":false,"in":"query","description":"Only items of this assistant","schema":{"format":"uuid","type":"string"}},{"name":"search","required":false,"in":"query","description":"Case-insensitive match on the name","schema":{"type":"string"}},{"name":"skip","required":false,"in":"query","description":"Number of items to skip (pagination)","schema":{"example":0,"type":"number"}},{"name":"take","required":false,"in":"query","description":"Page size","schema":{"example":20,"type":"number"}}],"responses":{"200":{"description":"Page of knowledge items","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeListResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"List knowledge items","tags":["Knowledge Base"]},"post":{"description":"Adds a source to an assistant's knowledge base:\n\n- `text` — pass the text in `resourceContent`.\n- `url` — pass `resourceUrl`; the page is crawled and always indexed for search.\n- `file` — pass `fileId` of an uploaded file; its text is extracted into `resourceContent`.\n\nWith `isVector: false` (default) the content is given to the assistant as is — best for short texts. With `isVector: true` it is split into chunks and the assistant searches it — use it for large documents and catalogs.\n\nSearchable items (`isVector: true`, and every `url` item) are chunked and indexed in the background after the request returns: `indexStatus` starts as `pending` and becomes `indexed` (or `failed`). Poll **Get a knowledge item** to see when it is ready.","operationId":"createKnowledge","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssistantKnowledgeNewDto"}}}},"responses":{"201":{"description":"The created knowledge item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Create a knowledge item","tags":["Knowledge Base"]}},"/api/assistant-knowledge-new/{id}":{"get":{"description":"Returns one knowledge item with its content and indexing status. For `file` items the file metadata is included.\n\nSearchable items (`isVector: true`, and every `url` item) are chunked and indexed in the background after the request returns: `indexStatus` starts as `pending` and becomes `indexed` (or `failed`). Poll **Get a knowledge item** to see when it is ready.","operationId":"getKnowledge","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The knowledge item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Get a knowledge item","tags":["Knowledge Base"]},"patch":{"description":"Partially updates a knowledge item: only the fields you send change. Changing the content, URL, file or chunking re-indexes the item.\n\nSearchable items (`isVector: true`, and every `url` item) are chunked and indexed in the background after the request returns: `indexStatus` starts as `pending` and becomes `indexed` (or `failed`). Poll **Get a knowledge item** to see when it is ready.","operationId":"updateKnowledge","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAssistantKnowledgeNewDto"}}}},"responses":{"200":{"description":"The updated knowledge item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Update a knowledge item","tags":["Knowledge Base"]},"delete":{"description":"Removes the item and its search index from the assistant's knowledge base.","operationId":"deleteKnowledge","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Knowledge item deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeDeletedResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Delete a knowledge item","tags":["Knowledge Base"]}},"/api/assistant-knowledge-new/query":{"post":{"description":"Runs the same semantic search the assistant uses over its indexed knowledge and returns the matching chunks with similarity scores — use it to check why the assistant did or didn't find something. Only indexed items are searched. Each search spends embedding credits.","operationId":"searchKnowledge","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeQueryDto"}}}},"responses":{"201":{"description":"Matching chunks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Search the knowledge base","tags":["Knowledge Base"]}},"/api/chats/messengers":{"get":{"description":"Active channel connections of your company (Telegram, Instagram, WhatsApp, Viber, web widget, marketplaces…). `config.externalId` is the account ID that appears as `fromId` on its chats — use it to filter `GET /chats` by account. Credentials are never returned.","operationId":"listChannels","parameters":[{"name":"companyId","required":false,"in":"query","description":"Company to list channels of. Optional with an Access Token — it is already scoped to one company.","schema":{"format":"uuid"}}],"responses":{"200":{"description":"Connected channels","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelListModel"}}}},"401":{"description":"Missing or invalid credentials"}},"security":[{"access-token":[]}],"summary":"List connected channels","tags":["Chats"]}},"/api/chats":{"get":{"description":"Conversations of your company across every connected channel, most recent activity first.\n\n**Pagination.** Pass `take` (max 100) and follow `nextCursor` from the response as `cursor` — this is stable while new messages arrive. `skip` (offset) still works but can skip or repeat chats when the list reorders.\n\n**Filtering.** Use the simple query params (`messenger`, `assistantId`, `isUnderAiControl`, `unread`, …) or the `filter` expression for ranges and lists; both can be combined.\n\nAdd `include=lastMessage` to get the latest message of every chat in one request.","operationId":"listChats","parameters":[{"name":"skip","required":false,"in":"query","description":"Legacy offset pagination: number of chats to skip. Prefer `cursor`.","schema":{"example":0,"type":"number"}},{"name":"take","required":false,"in":"query","description":"Page size, 1–100","schema":{"example":20,"type":"number"}},{"name":"filter","required":false,"in":"query","description":"Filter expression: `;`-separated `field:op:value` conditions. Fields: `messenger` (eq, in), `assistantId` (eq, in), `managerId` (eq, in; `me`, `null`), `isUnderAiControl` (eq), `unread` (eq), `chatType` (eq, in), `phoneNumber` (like, startsWith), `lastMessageAt` / `createdAt` (gte, lte, between; YYYY-MM-DD). Lists are comma-separated; escape `;` `,` `\\` with `\\`.","schema":{"example":"messenger:in:telegram,instagram;lastMessageAt:gte:2026-10-01","type":"string"}},{"name":"search","required":false,"in":"query","description":"Case-insensitive substring over customer name, your account name, username and phone number","schema":{"example":"olena","type":"string"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field (default `lastMessageAt`). A non-default sort switches to offset pagination.","schema":{"enum":["lastMessageAt","createdAt"],"type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort direction (default `DESC`)","schema":{"enum":["ASC","DESC"],"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque keyset cursor from the previous response (`nextCursor`)","schema":{"type":"string"}},{"name":"include","required":false,"in":"query","description":"Comma-separated extras: `lastMessage` — latest message of each chat; `assistant`, `manager` — the related objects","schema":{"example":"lastMessage","type":"string"}},{"name":"withCount","required":false,"in":"query","description":"`\"false\"` skips computing `totalCount` — faster when you only follow `nextCursor`","schema":{"example":"false","type":"string"}},{"name":"companyId","required":false,"in":"query","description":"Company to list chats of. Optional with an Access Token — it is already scoped to one company.","schema":{"format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","type":"string"}},{"name":"messenger","required":false,"in":"query","description":"Filter by messenger channel","schema":{"example":"telegram","type":"string","enum":["instagram","telegram","telegram-bot","messenger","whatsapp","whatsapp-business","viber-echat","whatsapp-echat","custom-messenger","widget","prom","rozetka","olx","directhub","sendpulse","sitniks","gmail","resend","playground"]}},{"name":"sourceChannel","required":false,"in":"query","description":"Filter by the origin channel of aggregator chats (chat.meta.sourceChannel)","schema":{"example":"telegram","type":"string"}},{"name":"fromId","required":false,"in":"query","description":"Only chats of one connected account — the `config.externalId` from `GET /chats/messengers`","schema":{"example":"6340798777","type":"string"}},{"name":"assistantId","required":false,"in":"query","description":"Filter by assigned assistant ID","schema":{"format":"uuid","example":"b2c3d4e5-f6a7-8901-bcde-f23456789012","type":"string"}},{"name":"unread","required":false,"in":"query","description":"Filter by unread state: \"true\" — only chats with unread messages, \"false\" — only fully read chats","schema":{"example":"true","type":"string"}},{"name":"isUnderAiControl","required":false,"in":"query","description":"Filter by AI control: \"true\" — only chats handled by AI, \"false\" — only chats handled by humans","schema":{"example":"true","type":"string"}},{"name":"managerId","required":false,"in":"query","description":"Filter by assigned manager: a user UUID, \"me\" (current user), or \"null\" (chats without a manager)","schema":{"example":"me","type":"string"}}],"responses":{"200":{"description":"A page of chats","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatListModel"}}}},"400":{"description":"Invalid filter expression or query parameter"},"401":{"description":"Missing or invalid credentials"}},"security":[{"access-token":[]}],"summary":"List chats","tags":["Chats"]},"post":{"description":"Registers a conversation record for a customer on one of your channels — e.g. to import a dialog from an external system or attach messages to it. No message is sent to the customer: chats are normally created automatically when a customer writes in. To message the customer afterwards use `POST /messages` with the returned `id`.","operationId":"createChat","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChatDto"}}}},"responses":{"201":{"description":"The created chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"400":{"description":"Invalid body"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"`companyId` is not your company"}},"security":[{"access-token":[]}],"summary":"Create a chat","tags":["Chats"]}},"/api/chats/{id}":{"get":{"description":"One conversation with its state: channel, customer, assigned assistant, AI on/off, unread counter, labels and comments. Messages are not included — use `GET /messages?chatId=…`.","operationId":"getChat","parameters":[{"name":"include","required":false,"in":"query","description":"Comma-separated related objects to embed: `assistant`, `manager`","schema":{"example":"assistant","type":"string"}},{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"The chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found or belongs to another company"}},"security":[{"access-token":[]}],"summary":"Get a chat","tags":["Chats"]},"patch":{"description":"Partial update: rename the chat, switch the assistant or the AI flag. Prefer the dedicated endpoints (`assign-assistant`, `ai-control`) — they validate the assistant and the subscription; this one writes the fields as given.","operationId":"updateChat","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChatDto"}}}},"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"400":{"description":"Invalid body"},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Update a chat","tags":["Chats"]},"delete":{"description":"Permanently deletes the conversation with all its messages and attachments on our side. Nothing is deleted in the channel itself. Cannot be undone; if the customer writes again, a new chat is created.","operationId":"deleteChat","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"Chat deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatDeletedModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Delete a chat","tags":["Chats"]}},"/api/chats/{id}/ai-control":{"patch":{"description":"`isUnderAiControl: true` lets the assigned assistant answer the customer; `false` hands the chat to a human — the assistant stops replying until it is turned back on. When turning off you may pass `aiDisableReason` (defaults to `manual`).\n\nTurning the AI on requires an active subscription (403 otherwise) and is not allowed in group chats (400). Turning it off always works.","operationId":"setChatAiControl","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"description":"AI control state and optional disable reason","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleAiControlDto"},"examples":{"enable":{"summary":"Turn the AI on","value":{"isUnderAiControl":true}},"disableManual":{"summary":"Hand over to a human","value":{"isUnderAiControl":false,"aiDisableReason":"manual"}}}}}},"responses":{"200":{"description":"New AI state of the chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatAiControlResultModel"}}}},"400":{"description":"Group chat, or invalid body"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"Turning the AI on requires an active subscription"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Turn the AI on or off in a chat","tags":["Chats"]}},"/api/chats/{id}/assign-assistant":{"patch":{"description":"Makes the assistant answer this chat and turns the AI on in the same call (`isUnderAiControl` becomes `true`). The assistant must belong to the same company; draft variants and group chats are rejected.","operationId":"assignChatAssistant","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignAssistantDto"}}}},"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatAssistantResultModel"}}}},"400":{"description":"Assistant not found, belongs to another company, is a draft, or the chat is a group"},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Assign an assistant to a chat","tags":["Chats"]}},"/api/chats/{id}/unassign-assistant":{"patch":{"description":"Clears `assistantId`, so no assistant answers this chat. `isUnderAiControl` is left as is — call `PATCH /chats/{id}/ai-control` to turn it off explicitly.","operationId":"unassignChatAssistant","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatAssistantResultModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Remove the assistant from a chat","tags":["Chats"]}},"/api/chats/{id}/read":{"post":{"description":"Resets `unreadCount` to 0 and sets `lastReadAt` to now. Nothing is sent to the customer.","operationId":"markChatRead","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"201":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Mark a chat as read","tags":["Chats"]}},"/api/chats/{id}/unread":{"post":{"description":"Moves `lastReadAt` back to your latest outgoing message and recounts customer messages after it as unread (at least 1). Nothing is sent to the customer.","operationId":"markChatUnread","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"201":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Mark a chat as unread","tags":["Chats"]}},"/api/chats/{id}/meta/labels":{"post":{"description":"Attaches a tag by name: the tag is created in the company dictionary when missing (see `/chat-tags`) and linked to the chat. Adding a label the chat already has is a no-op. Prefer `POST /chats/{id}/tags` when you know the tag id.","operationId":"addChatLabel","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddChatLabelDto"}}}},"responses":{"201":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"400":{"description":"Label is empty or longer than 64 characters"},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Add a label to a chat","tags":["Chats"]}},"/api/chats/{id}/meta/labels/{label}":{"delete":{"description":"Detaches the tag with this name from the chat. Removing a label the chat doesn't have is a no-op.","operationId":"removeChatLabel","parameters":[{"name":"label","required":true,"in":"path","description":"Label to remove (URL-encoded)","schema":{"example":"vip","type":"string"}},{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Remove a label from a chat","tags":["Chats"]}},"/api/chats/{id}/folder":{"patch":{"description":"Files the chat into one of the company's folders; `folderId: null` takes it out of its folder.","operationId":"setChatFolder","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetChatFolderDto"}}}},"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"400":{"description":"The folder does not exist in this company"},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Move a chat into a folder","tags":["Chats"]}},"/api/chats/{id}/tags":{"put":{"description":"The chat ends up with exactly the given tags.","operationId":"setChatTags","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetChatTagsDto"}}}},"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"400":{"description":"A tag id does not exist in this company"},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Replace the tags of a chat","tags":["Chats"]},"post":{"description":"Attaching a tag the chat already has is a no-op.","operationId":"addChatTag","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkChatTagDto"}}}},"responses":{"201":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"400":{"description":"The tag does not exist in this company"},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Attach a tag to a chat","tags":["Chats"]}},"/api/chats/{id}/tags/{tagId}":{"delete":{"description":"Detaching a tag the chat does not have is a no-op.","operationId":"removeChatTag","parameters":[{"name":"tagId","required":true,"in":"path","description":"Tag ID","schema":{"format":"uuid","type":"string"}},{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Detach a tag from a chat","tags":["Chats"]}},"/api/chat-folders":{"get":{"description":"Folders your team files chats into. A chat sits in at most one folder; filter the chat list with `filter=folderId:eq:<id>` (or `folderId:eq:null` for unfiled chats).","operationId":"getChatFolders","parameters":[{"name":"companyId","required":false,"in":"query","description":"Company to list folders for","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Folders of the company","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatFolderModel"}}}}},"401":{"description":"Missing or invalid credentials"},"403":{"description":"No access to this company"}},"security":[{"access-token":[]}],"summary":"List chat folders","tags":["Chats"]},"post":{"description":"Adds a folder to the company. Chats are filed into it with `PATCH /chats/{id}/folder`.","operationId":"createChatFolder","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChatFolderDto"}}}},"responses":{"201":{"description":"The created folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatFolderModel"}}}},"400":{"description":"Name is empty or longer than 80 characters, or color is not a hex color"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"No access to this company"}},"security":[{"access-token":[]}],"summary":"Create a chat folder","tags":["Chats"]}},"/api/chat-folders/{id}":{"patch":{"description":"Changes the folder's name and/or color. The chats in it are untouched.","operationId":"updateChatFolder","parameters":[{"name":"id","required":true,"in":"path","description":"Folder ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChatFolderDto"}}}},"responses":{"200":{"description":"The updated folder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatFolderModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Folder not found"}},"security":[{"access-token":[]}],"summary":"Rename or recolor a chat folder","tags":["Chats"]},"delete":{"description":"The chats in it are kept and become unfiled.","operationId":"deleteChatFolder","parameters":[{"name":"id","required":true,"in":"path","description":"Folder ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"Folder deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatDeletedModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Folder not found"}},"security":[{"access-token":[]}],"summary":"Delete a chat folder","tags":["Chats"]}},"/api/chat-tags":{"get":{"description":"The company's tag dictionary. A chat carries any number of tags; filter the chat list with `filter=tagIds:in:<id>,<id>` (chats having any of them).","operationId":"getChatTags","parameters":[{"name":"companyId","required":false,"in":"query","description":"Company to list tags for","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Tags of the company","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatTagModel"}}}}},"401":{"description":"Missing or invalid credentials"},"403":{"description":"No access to this company"}},"security":[{"access-token":[]}],"summary":"List chat tags","tags":["Chats"]},"post":{"description":"Names are unique per company regardless of case.","operationId":"createChatTag","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChatTagDto"}}}},"responses":{"201":{"description":"The created tag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatTagModel"}}}},"400":{"description":"Name is empty or longer than 64 characters, or color is not a hex color"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"No access to this company"},"409":{"description":"A tag with this name already exists"}},"security":[{"access-token":[]}],"summary":"Create a chat tag","tags":["Chats"]}},"/api/chat-tags/{id}":{"patch":{"description":"Renaming keeps every chat link. AI-suggestion exclusions (`excludedTags`) match tags by name, so a renamed tag drops out of that list until it is re-added.","operationId":"updateChatTag","parameters":[{"name":"id","required":true,"in":"path","description":"Tag ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChatTagDto"}}}},"responses":{"200":{"description":"The updated tag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatTagModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Tag not found"},"409":{"description":"A tag with this name already exists"}},"security":[{"access-token":[]}],"summary":"Rename or recolor a chat tag","tags":["Chats"]},"delete":{"description":"Removes the tag from every chat that carries it.","operationId":"deleteChatTag","parameters":[{"name":"id","required":true,"in":"path","description":"Tag ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"Tag deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatDeletedModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Tag not found"}},"security":[{"access-token":[]}],"summary":"Delete a chat tag","tags":["Chats"]}},"/api/chats/{id}/meta/comments":{"post":{"description":"Appends a note to `meta.comments`, visible to your team only — never sent to the customer. The comment gets an `id` (use it to delete) and, when added with a user JWT, the author's `authorId`.","operationId":"addChatComment","parameters":[{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddChatCommentDto"}}}},"responses":{"201":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"400":{"description":"Text is empty or longer than 2000 characters"},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Add an internal comment to a chat","tags":["Chats"]}},"/api/chats/{id}/meta/comments/{commentId}":{"delete":{"description":"Removes the comment from `meta.comments`. Deleting a comment that doesn't exist is a no-op.","operationId":"removeChatComment","parameters":[{"name":"commentId","required":true,"in":"path","description":"Comment ID from `meta.comments[].id`","schema":{"format":"uuid","type":"string"}},{"name":"id","required":true,"in":"path","description":"Chat ID","schema":{"format":"uuid"}}],"responses":{"200":{"description":"The updated chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModel"}}}},"401":{"description":"Missing or invalid credentials"},"404":{"description":"Chat not found"}},"security":[{"access-token":[]}],"summary":"Delete an internal comment","tags":["Chats"]}},"/api/messages":{"get":{"description":"Message history of one chat, newest first, including attachments (`messageMedia`) and the quoted message for replies.\n\n**Pagination.** Pass `take` (default 30) and follow `nextCursor` as `cursor` to load older messages — stable while new messages arrive. Legacy: `skip` switches to offset paging; a request with neither `take`, `cursor` nor `skip` returns the whole history at once.\n\nThe history also contains the assistant's tool frames (`type` = `FunctionCall` / `FunctionCallOutput`) — filter them out if you only need the visible dialogue.","operationId":"listMessages","parameters":[{"name":"skip","required":false,"in":"query","description":"Legacy offset pagination. Prefer `cursor`: offsets drop messages when new ones arrive.","schema":{"example":0,"type":"number"}},{"name":"take","required":false,"in":"query","description":"Page size (default 30 in cursor mode)","schema":{"example":30,"type":"number"}},{"name":"chatId","required":true,"in":"query","description":"ID of the chat to list messages from","schema":{"format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from the previous response (`nextCursor`)","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of messages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageListModel"}}}},"400":{"description":"`chatId` is missing or not a UUID"},"401":{"description":"Missing or invalid credentials"},"403":{"description":"The chat belongs to another company"}},"security":[{"access-token":[]}],"summary":"List messages of a chat","tags":["Messages"]},"post":{"description":"Stores a message in the chat and delivers it to the customer through the chat's channel (Telegram, Instagram, WhatsApp, widget…), as your connected account. Delivery is asynchronous: the response returns the stored message right away, before the channel confirms it.\n\n**What to send.** `chatId`, `text` (or `mediaIds`), and the fields the request schema marks required. To show up as *your side* of the conversation — the same as a team member typing in the inbox — send `role: \"User\"` with `fromId` = the chat's `fromId`. The stored `source` is always `PLATFORM`, whatever you pass.\n\n**Attachments.** `mediaIds` (up to 10) reference files already uploaded to your company's media storage; text becomes the caption where the channel supports it.\n\n**Replies.** `replyToMessageId` quotes an earlier message of the same chat — only on channels that support outbound replies.\n\nWithout `chatId` the message is only stored, never delivered.","operationId":"sendMessage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMessageDto"},"examples":{"text":{"summary":"Text message","value":{"chatId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","text":"Your order #1042 has shipped!","role":"User","type":"Text","source":"API","fromId":"6340798777"}},"photo":{"summary":"Photo with caption","value":{"chatId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","text":"Here is the dress in red","mediaIds":["c3d4e5f6-a7b8-9012-cdef-345678901234"],"role":"User","type":"Photo","source":"API","fromId":"6340798777"}}}}}},"responses":{"201":{"description":"The stored message, queued for delivery","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageModel"}}}},"400":{"description":"Invalid body, unknown `mediaIds`, or a reply the channel cannot deliver (unsupported channel, other chat, attachments on a text-only reply channel)"},"403":{"description":"The chat belongs to another company"}},"security":[{"access-token":[]}],"summary":"Send a message","tags":["Messages"]}},"/api/media/upload":{"post":{"description":"Uploads a file to attach to a message. Send the returned `id` in `mediaIds` of `POST /api/messages`.\n\nMax size 25 MB. Allowed types: images, video, audio, PDF, Word/Excel/PowerPoint, ZIP, plain text and CSV; anything else returns 415.","operationId":"uploadMedia","parameters":[{"name":"companyId","required":false,"in":"query","description":"Company to store the file in. Defaults to the Access Token's company.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file to upload"}}}}}},"responses":{"201":{"description":"Uploaded file","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaModel"}}}}},"security":[{"access-token":[]}],"summary":"Upload a file","tags":["Messages"]}},"/api/messages/{id}":{"get":{"description":"One message by ID, with its chat reference. Use it to re-read a message you received in a webhook.","operationId":"getMessage","parameters":[{"name":"id","required":true,"in":"path","description":"Message ID","schema":{"format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}}],"responses":{"200":{"description":"The message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageModel"}}}},"404":{"description":"Message not found or belongs to another company"}},"security":[{"access-token":[]}],"summary":"Get a message","tags":["Messages"]}},"/api/assistants/{id}/originate":{"post":{"description":"The assistant calls the given phone number. `{id}` is the assistant id from the Assistants API; the assistant needs a phone number attached.\n\n**The call is asynchronous.** A 200 response means the request was accepted, not that the call has started. The call enters the telephony dial queue and starts as soon as a line is free — usually within seconds, but under load it may wait up to ~5 minutes; if the wait expires, the call ends with the `not_dialed` status without dialing.\n\n**Getting the result.** When the call finishes, a call record with status, duration, recording and transcript appears in `GET /api/calls-history` (filter by `assistantId`, `phone` or `status`; then `GET /api/calls-history/{id}` for the transcript). The `dynamicVariables` you send come back in the record's `dynamicVariables` (alongside system variables such as `phone_number`), so put your own reference (e.g. `order_id`) there to match the call to your data.\n\n**Personalization.** `dynamicVariables` are substituted into `{{placeholders}}` in the assistant's prompt for this call only.\n\nRequires an active subscription (403 otherwise) and a positive voice balance (400 otherwise).","operationId":"startCall","parameters":[{"name":"id","required":true,"in":"path","description":"Assistant id (Assistants API)","schema":{"example":"123e4567-e89b-12d3-a456-426614174000","type":"string"}}],"requestBody":{"required":true,"description":"Target phone number, optional caller assistant override, and optional `dynamicVariables` forwarded to the voice assistant as `dynamic_variables` (key/value map for personalizing the conversation: customer name, order id, etc.). `callerAssistantId` must belong to the same company as the route assistant; when omitted, the route assistant is used as the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OriginateCallDto"},"examples":{"full":{"summary":"All fields (phone + caller override + dynamic variables)","value":{"phoneNumber":"+380501234567","callerAssistantId":"123e4567-e89b-12d3-a456-426614174000","dynamicVariables":{"customer_name":"John Doe","order_id":"ORD-12345","preferred_language":"en"}}},"withDynamicVariables":{"summary":"With dynamic variables (personalization)","value":{"phoneNumber":"+380501234567","dynamicVariables":{"customer_name":"John Doe","order_id":"ORD-12345","preferred_language":"en"}}},"withCallerAssistant":{"summary":"Override caller assistant","value":{"phoneNumber":"+380501234567","callerAssistantId":"123e4567-e89b-12d3-a456-426614174000"}},"minimal":{"summary":"Minimal — only phone number","value":{"phoneNumber":"+380501234567"}}}}}},"responses":{"200":{"description":"Call request accepted for processing (the call itself starts asynchronously)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"The call request was accepted and queued for dialing"},"workflowId":{"type":"string","example":"originate-call-123e4567-e89b-12d3-a456-426614174000-1717600000000","description":"Internal identifier of the accepted call request (useful when contacting support)"}}}}}},"400":{"description":"Invalid phone number format, missing required fields, or the call is blocked by a low account balance"},"401":{"description":"Unauthorized - Missing or invalid token or insufficient permissions"},"403":{"description":"The company has no active subscription"}},"security":[{"access-token":[]}],"summary":"Start an outbound call","tags":["Calls"]}},"/api/calls-history":{"get":{"description":"Returns the company's calls, newest first, with status, duration, recording URL and transcript text. Paginate with `skip` / `take` (max 100).\n\nFilter with `filter=field:op:value;…` — fields: `status` (eq, in), `assistantId` (eq, in), `source` (eq, in, nin), `callDate` (gte, lte, between), `duration` in seconds (gte, lte, between). Example: `filter=assistantId:eq:<id>;status:eq:transcribed;callDate:gte:2026-10-01`. `search` matches the customer phone number or the employee name. Sort with `sortBy` = `callDate` | `duration` and `sortOrder`.\n\nUse `GET /api/calls-history/{id}` for the transcript split into replies.","operationId":"listCalls","parameters":[{"name":"skip","required":false,"in":"query","description":"Number of items to skip for pagination","schema":{"example":0,"type":"number"}},{"name":"take","required":false,"in":"query","description":"Number of items to return","schema":{"example":20,"type":"number"}},{"name":"filter","required":false,"in":"query","description":"Filter expression: semicolon-separated `field:op:value` conditions, e.g. `status:in:active,trial;createdAt:between:2026-08-01,2026-08-20`. Operators: eq, ne, gt, gte, lt, lte, like, startsWith, in, nin, between, isNull, isNotNull. `\\` escapes `\\`, `;` and `,`. Fields, operators and values are validated against GET /filters; anything else is a 400.","schema":{"example":"status:in:active,trial;createdAt:gte:2026-08-01","type":"string"}},{"name":"search","required":false,"in":"query","description":"Free-text search over the resource's search fields","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field; must be one of the resource's sortFields","schema":{"type":"string"}},{"name":"sortOrder","required":false,"in":"query","schema":{"type":"string","enum":["ASC","DESC"]}},{"name":"cursor","required":false,"in":"query","description":"Opaque keyset cursor from the previous response (`nextCursor`)","schema":{"type":"string"}},{"name":"include","required":false,"in":"query","description":"Comma-separated relations to include","schema":{"type":"string"}},{"name":"withCount","required":false,"in":"query","description":"\"false\" skips the total count — for infinite-scroll clients that only follow `nextCursor`","schema":{"type":"string"}},{"name":"companyId","required":true,"in":"query","description":"Company id (required; with an Access Token it must be the token's company)","schema":{"format":"uuid","type":"string"}},{"name":"phone","required":false,"in":"query","description":"Customer phone number, partial match; a leading `+` is ignored","schema":{"example":"+380501234567","type":"string"}},{"name":"companyIntegrationId","required":false,"in":"query","description":"Only calls that came through this telephony integration","schema":{"format":"uuid","type":"string"}},{"name":"excludeSource","required":false,"in":"query","description":"Exclude calls from this source","schema":{"type":"string","enum":["eleven_labs","voice_assistant","va","phonet","binotel","unitalk","ringostat","custom_calls","streamtele"]}}],"responses":{"200":{"description":"A page of calls","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallListResponseDto"}}}},"400":{"description":"Bad request - missing `companyId` or an invalid filter expression"}},"security":[{"access-token":[]}],"summary":"List calls","tags":["Calls"]}},"/api/calls-history/{id}":{"get":{"description":"Returns one call with its recording URL, full transcript (`transcriptText`, `transcription`) and the transcript split into `replies` in conversation order.","operationId":"getCall","parameters":[{"name":"id","required":true,"in":"path","description":"Call id from List calls","schema":{"format":"uuid"}}],"responses":{"200":{"description":"The call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallDetailResponseDto"}}}},"404":{"description":"Call not found"}},"security":[{"access-token":[]}],"summary":"Get a call","tags":["Calls"]}},"/api/integrations/chat-webhook/{companyId}/connect":{"post":{"description":"Subscribe your server to chat messages. After this call Happ POSTs every new message — incoming from a customer, sent by the AI assistant or by a manager — to `webhookUrl`. The payload and signature are described in the **Webhooks** section.\n\nWithout `chatOwnerIds` you receive messages of every chat of the company. Pass the external account IDs of your connected channels (the `chatOwnerId` of a chat) to receive only those conversations.\n\nA company has one chat webhook: connecting again updates it (URL, `chatOwnerIds`, and the secret if one is sent) instead of adding a second subscription.","operationId":"connectChatWebhook","parameters":[{"name":"companyId","required":true,"in":"path","description":"Your company ID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectChatWebhookDto"}}}},"responses":{"201":{"description":"Webhook connected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyIntegrationResponseDto"}}}},"403":{"description":"The token has no access to this company"}},"security":[{"access-token":[]}],"summary":"Connect a chat webhook","tags":["Webhooks"]}},"/api/integrations/call-webhook/{companyId}/connect":{"post":{"description":"Subscribe your server to finished voice calls of the given assistants. When a call ends, Happ POSTs a `post_call` event with the summary, recording and transcript to `webhookUrl`. The payload and signature are described in the **Webhooks** section.\n\nCalls are matched by assistant: pass every assistant you want to hear from in `assistantIds`. An assistant reports to one URL at a time — connecting it to a new URL moves it there.\n\nConnecting the same URL again reuses the subscription: new assistants are added, a new `webhookSecret` replaces the old one.","operationId":"connectCallWebhook","parameters":[{"name":"companyId","required":true,"in":"path","description":"Your company ID","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectCallWebhookDto"}}}},"responses":{"201":{"description":"Webhook connected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyIntegrationResponseDto"}}}},"403":{"description":"The token has no access to this company or to one of the assistants"}},"security":[{"access-token":[]}],"summary":"Connect a call webhook","tags":["Webhooks"]}},"/api/integrations":{"get":{"description":"Integrations connected to your company. Pass `type=chat_webhook` to list chat webhooks. Only active integrations are returned unless `isActive=false` is passed.","operationId":"listIntegrations","parameters":[{"name":"companyId","required":false,"in":"query","description":"Filter by company ID","schema":{"type":"string"}},{"name":"isActive","required":false,"in":"query","description":"Filter by active status (default `true`)","schema":{"type":"boolean"}},{"name":"type","required":false,"in":"query","description":"Filter by integration type, e.g. `chat_webhook`","schema":{"enum":["telegram","telegram_push_notifications","slack_push_notifications","instagram","viber_echat","custom_messenger","custom_calls","voice_assistant","eleven_labs","google_spreadsheets_notifications","binotel","ringostat","phonet","unitalk","facebook","messenger","keycrm","nethunt","odoo","salesdrive","bookon","smartcrm","altegio","whatsapp_wasender","whatsapp_business","whatsapp_echat","openai","groq","claude","gemini","qdrant","chat_webhook","call_webhook","telegram_bot","widget","mobile_push_notifications","email_notifications","instagram_comments","facebook_comments","sitniks","streamtele","prom","rozetka","rozetka_comments","olx","uspacy","roapp","keepincrm","creatio","zoho","directhub","mrcrm","onebox","sendpulse","bitrix24","lpcrm","lakmus","cleverstaff","gmail","google_calendar","shopify","resend","woocommerce","hubspot","pipedrive","kommo","nova_poshta","boulevard"],"type":"string"}},{"name":"category","required":false,"in":"query","description":"Filter by integration category, e.g. `webhooks`","schema":{"enum":["messengers","push-notifications","telephony","crm","llm_models","vector_databases","webhooks","voice_assistant","comments","delivery"],"type":"string"}}],"responses":{"200":{"description":"Connected integrations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyIntegrationListResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"List connected integrations","tags":["Webhooks"]}},"/api/integrations/{id}":{"delete":{"description":"Deletes a connected integration — for a chat webhook, deliveries stop immediately. Returns `success: false` when the ID does not exist.","operationId":"disconnectIntegration","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Deletion result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteIntegrationResponseDto"}}}}},"security":[{"access-token":[]}],"summary":"Disconnect an integration","tags":["Webhooks"]}}},"info":{"title":"Happ Platform API - Public","description":"## Happ Platform API\n\nREST API for AI assistants that answer customers in messengers (Telegram, Instagram, WhatsApp, Viber, web widget) and on the phone. Configure assistants, read and send chat messages, start outbound calls and receive events on your server.\n\n### Authentication\n\n1. Sign in (`POST /api/auth/sign-in`) to get a JWT — or open your account at [my.happ.tools](https://my.happ.tools).\n2. Generate an **Access Token**: `POST /api/companies/{companyId}/access-tokens` (JWT required).\n3. Send it on every request:\n   ```\n   X-Access-Token: happ_your_token_here\n   ```\n\nAn Access Token is scoped to a **single company** — it only reaches that company's assistants, chats and calls.\n\nUse the Access Token to integrate. JWT (`Authorization: Bearer <jwt>`) is for the web/admin panel and the bootstrap auth endpoints only.\n\n### Errors\n\nErrors use standard HTTP status codes with a JSON body:\n\n```json\n{\n  \"statusCode\": 404,\n  \"message\": \"Chat not found\",\n  \"requestId\": \"f1c2d3e4-...\",\n  \"timestamp\": \"2026-10-07T09:30:00.000Z\",\n  \"path\": \"/api/chats/123\"\n}\n```\n\n`400` — invalid request (`message` lists the failed fields), `401` — missing or invalid token, `403` — the token has no access to this resource, `404` — not found, `5xx` — server error (quote `requestId` when contacting support).\n\n### Guides & interactive examples\n\nStep-by-step guides, quickstart and live examples: **[docs.happ.tools](https://docs.happ.tools/en/docs/api)**\n\n### Building with an AI assistant\n\nUsing Claude, Cursor or Copilot to integrate? Point it at the machine-readable spec — `GET /api/swagger-json` — and the docs index [docs.happ.tools/llms.txt](https://docs.happ.tools/llms.txt). Both are kept in sync with this reference.","version":"1.0","contact":{}},"tags":[{"name":"Authentication","description":"Sign in with email and password to get a JWT. The JWT is only needed to mint an Access Token (see Access Tokens); integrations then authenticate with the `X-Access-Token` header."},{"name":"Access Tokens","description":"Long-lived API keys (`happ_...`) scoped to one company. Create one with a JWT, then send it as `X-Access-Token` on every other request. The full token value is returned only once, on creation."},{"name":"Companies","description":"A company is a workspace: assistants, chats, calls and tokens all belong to one. An Access Token reaches only its own company."},{"name":"Assistants","description":"AI assistants: system prompt, model, voice and behaviour settings. One assistant can answer chats in connected messengers and handle voice calls."},{"name":"Assistant Tools","description":"Webhook tools an assistant can call during a conversation — e.g. check order status in your system. The assistant sends the arguments it extracted to your URL and uses the response in its answer."},{"name":"Knowledge Base","description":"Knowledge items (text, files, URLs) the assistant searches to ground its answers. Items are chunked and indexed asynchronously after creation."},{"name":"Chats","description":"Conversations with customers across every connected channel (Telegram, Instagram, WhatsApp, Viber, web widget…). Control whether the AI answers, which assistant is assigned, labels and internal comments."},{"name":"Messages","description":"Messages inside a chat. Sending a message delivers it to the customer through the chat's channel."},{"name":"Calls","description":"Voice calls: start an outbound call by an assistant, then read the call history with status, duration, recording and transcript."},{"name":"Webhooks","description":"Receive chat messages and finished calls on your server in real time.\n\n- **Chat webhook** — `POST /api/integrations/chat-webhook/{companyId}/connect`: every new message in the company's chats.\n- **Call webhook** — `POST /api/integrations/call-webhook/{companyId}/connect`: every finished call of the assistants you pass.\n\n## Chat message event\n\nSent for **every new message** — from the customer, from the AI assistant and from managers. Use `direction` to tell them apart.\n\n### Request\n\n```http\nPOST /your/webhook HTTP/1.1\nContent-Type: application/json\nUser-Agent: Happ-Platform-API/1.0\nX-Signature: sha256=5d41402abc4b2a76b9719d911017c592...\n```\n\n```json\n{\n  \"messageId\": \"7c9e6679-7425-40de-944b-e07fc1f90ae7\",\n  \"chatId\": \"1b4e28ba-2fa1-11d2-883f-0016d3cca427\",\n  \"companyId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"from\": \"987654321\",\n  \"text\": \"Hi! Is the blue jacket available in size M?\",\n  \"createdAt\": \"2026-10-07T09:30:00.000Z\",\n  \"updatedAt\": \"2026-10-07T09:30:00.000Z\",\n  \"chatType\": \"private\",\n  \"chatTitle\": \"Olena Kovalenko\",\n  \"chatOwnerId\": \"123456789\",\n  \"direction\": \"client\",\n  \"replyTo\": { \"messageId\": \"0f8fad5b-d9cb-469f-a165-70867728950e\" }\n}\n```\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `messageId` | string | Message ID — fetch it with `GET /api/messages/{id}` |\n| `chatId` | string | Chat ID — fetch it with `GET /api/chats/{id}` |\n| `companyId` | string | Your company ID |\n| `from` | string | External account ID of the sender in the channel |\n| `text` | string | Message text; empty string for media-only messages |\n| `createdAt`, `updatedAt` | string | ISO 8601 timestamps |\n| `chatType` | string | `private`, `group` or `channel` |\n| `chatTitle` | string | Chat name — the customer's name for a private chat |\n| `chatOwnerId` | string | External ID of your connected account the chat belongs to |\n| `direction` | string | `client` — written by the customer; `own` — by your side (AI assistant or manager); `unknown` — could not be determined |\n| `groupAccountIds` | string[] | Group chats only: your connected accounts present in the group |\n| `senderName` | string | Group chats only: display name of the author |\n| `replyTo` | object | Present when the message is a reply: `messageId` (Happ ID, if the original is known) and/or `externalId` (the channel's ID) |\n\nOptional fields are omitted, not sent as `null`.\n\n## Call event\n\nSent once a call of a subscribed assistant has ended and been processed. Calls that never connected are not sent.\n\n```json\n{\n  \"event_type\": \"post_call\",\n  \"call_uuid\": \"va-call-3f1b2c4d\",\n  \"timestamp\": 1791366600,\n  \"companyId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"assistantId\": \"7c9e6679-7425-40de-944b-e07fc1f90ae7\",\n  \"assistantPhone\": \"+380443334455\",\n  \"externalNumber\": \"+380501234567\",\n  \"direction\": \"outbound\",\n  \"duration\": 94,\n  \"terminationReason\": \"Call ended by remote party\",\n  \"mainLanguage\": \"uk\",\n  \"summary\": \"The customer confirmed order #1042 and asked for delivery on Friday.\",\n  \"summaryTitle\": \"Order confirmed\",\n  \"callSuccessful\": \"success\",\n  \"recordingUrl\": \"https://cdn.happ.tools/recordings/3f1b2c4d.mp3\",\n  \"transcript\": [\n    { \"speaker\": \"agent\", \"text\": \"Hello! This is Happ Store calling about your order.\", \"startTimeSecs\": 0, \"endTimeSecs\": null },\n    { \"speaker\": \"client\", \"text\": \"Yes, hello.\", \"startTimeSecs\": 3, \"endTimeSecs\": null }\n  ]\n}\n```\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `event_type` | string | Always `post_call` |\n| `call_uuid` | string | Unique call identifier — use it to deduplicate deliveries |\n| `timestamp` | number | Call time, Unix seconds |\n| `companyId`, `assistantId` | string | Your company and the assistant that handled the call |\n| `assistantPhone` | string \\| null | The assistant's phone number |\n| `externalNumber` | string \\| null | The other party's phone number |\n| `direction` | string \\| null | `inbound` or `outbound` |\n| `duration` | number | Call length in seconds |\n| `terminationReason` | string \\| null | Why the call ended |\n| `mainLanguage` | string \\| null | Detected language code |\n| `summary`, `summaryTitle` | string \\| null | AI summary of the call and its short title |\n| `callSuccessful` | string \\| null | AI verdict on whether the call reached its goal: `success`, `failure` or `unknown` |\n| `recordingUrl` | string \\| null | Call recording |\n| `transcript` | array | Replies in order: `speaker` (`agent` or `client`), `text`, `startTimeSecs` (offset from the call start); `endTimeSecs` is reserved and currently `null` |\n\nNote the mixed naming: `event_type` and `call_uuid` are snake_case, the rest camelCase. The full call record is also available via `GET /api/calls-history`.\n\n### Verifying the signature\n\nBoth webhooks are signed the same way. When the webhook was connected with `webhookSecret`, every request carries `X-Signature: sha256=<hex>` — an HMAC-SHA256 of the **raw request body** with your secret. Compute it over the bytes you received, before parsing the JSON:\n\n```js\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\n\nfunction isValidSignature(rawBody, header, secret) {\n  const expected = \"sha256=\" + createHmac(\"sha256\", secret).update(rawBody, \"utf8\").digest(\"hex\");\n  return header?.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected));\n}\n```\n\nRequests also carry `Authorization: Basic <secret>` with the raw secret — kept for older integrations; verify the signature instead.\n\n### Delivery\n\n- Respond with any `2xx` status within **10 seconds**; the response body is ignored.\n- Chat messages are delivered **once**. Failed deliveries (timeout, non-2xx, network error) are not retried — to catch up after downtime, read missed messages with `GET /api/messages?chatId=...`.\n- Call events are delivered **at least once**: the same call may arrive more than once, deduplicate by `call_uuid`. Missed calls can be read with `GET /api/calls-history`.\n- Handle the request quickly and do heavy work asynchronously.\n- To stop deliveries, find the subscription with `GET /api/integrations?type=chat_webhook` (or `call_webhook`) and delete it with `DELETE /api/integrations/{id}`."}],"servers":[{"url":"https://api.happ.tools"}],"components":{"securitySchemes":{"jwt-auth":{"scheme":"bearer","bearerFormat":"JWT","type":"http","name":"JWT Token","description":"Enter JWT token from admin panel"},"access-token":{"type":"apiKey","in":"header","name":"X-Access-Token","description":"Enter your Access Token (format: happ_...)"}},"schemas":{"CompanyMemberResponseDto":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Member user id"},"companyId":{"type":"string","format":"uuid","description":"Company id"},"role":{"type":"string","enum":["owner","admin","member"],"description":"Member role in the company"},"user":{"type":"object","additionalProperties":true,"description":"Member user profile (id, email, firstName, lastName, …)"}},"required":["userId","companyId","role"]},"CompanyResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Company id"},"name":{"type":"string","description":"Company name","example":"Acme Store"},"description":{"type":"object","nullable":true,"description":"Company description"},"logoUrl":{"type":"object","nullable":true,"description":"Logo image URL"},"status":{"type":"string","enum":["new","trial","active","inactive","churned","paused","banned"],"description":"Account lifecycle status","example":"active"},"ownerId":{"type":"string","format":"uuid","description":"User id of the company owner"},"preferredCurrency":{"type":"object","nullable":true,"description":"Currency invoices are issued in","example":"UAH"},"companyUsers":{"description":"Company members with their roles","type":"array","items":{"$ref":"#/components/schemas/CompanyMemberResponseDto"}},"subscriptions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Subscriptions of the company with their plan configuration"},"createdAt":{"type":"string","format":"date-time","description":"Creation time"},"updatedAt":{"type":"string","format":"date-time","description":"Last update time"}},"required":["id","name","status","ownerId","companyUsers","subscriptions","createdAt","updatedAt"]},"CompanyListResponseDto":{"type":"object","properties":{"data":{"description":"Companies","type":"array","items":{"$ref":"#/components/schemas/CompanyResponseDto"}},"totalCount":{"type":"number","description":"Total number of companies","example":1},"page":{"type":"number","description":"Page number","example":1}},"required":["data","totalCount","page"]},"CreateAccessTokenDto":{"type":"object","properties":{"name":{"type":"string","description":"Name for identifying the token","example":"Zapier integration"},"description":{"type":"string","description":"Description of the token usage","example":"Used by the Zapier workflow that syncs new leads"}}},"AccessTokenResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Token id — use it to rename or revoke the token"},"tokenPrefix":{"type":"string","description":"First characters of the token, to recognise it in the list. The full value is never shown again","example":"happ_3f9a"},"companyId":{"type":"string","format":"uuid","description":"Company the token grants access to"},"name":{"type":"object","nullable":true,"description":"Name for identifying the token","example":"Zapier integration"},"description":{"type":"object","nullable":true,"description":"Description of the token usage","example":"Used by the Zapier workflow that syncs new leads"},"isActive":{"type":"boolean","description":"False once the token is revoked; revoked tokens are no longer listed","example":true},"lastUsedAt":{"type":"object","format":"date-time","nullable":true,"description":"Last time a request was authenticated with this token"},"expiresAt":{"type":"object","format":"date-time","nullable":true,"description":"Expiry time; null — the token does not expire until revoked"},"createdAt":{"type":"string","format":"date-time","description":"Creation time"},"updatedAt":{"type":"string","format":"date-time","description":"Last update time"}},"required":["id","tokenPrefix","companyId","isActive","createdAt","updatedAt"]},"CreatedAccessTokenResponseDto":{"type":"object","properties":{"accessToken":{"description":"The created token record","allOf":[{"$ref":"#/components/schemas/AccessTokenResponseDto"}]},"token":{"type":"string","description":"The token value to send as the `X-Access-Token` header. Returned only in this response — store it securely","example":"happ_3f9a6c1e8b7d4a2f9e0c5b1d7a3e8f2c"},"message":{"type":"string","description":"Human-readable confirmation","example":"Access token generated successfully. Save this token securely - it will not be shown again."}},"required":["accessToken","token","message"]},"UpdateAccessTokenDto":{"type":"object","properties":{"name":{"type":"string","description":"Name for identifying the token","example":"Zapier integration"}}},"RevokedAccessTokenResponseDto":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable confirmation","example":"Access token revoked successfully"}},"required":["message"]},"WorkScheduleIntervalDto":{"type":"object","properties":{"days":{"example":[1,2,3,4,5],"description":"ISO weekdays the interval STARTS on (1 = Monday … 7 = Sunday)","type":"array","items":{"type":"string"}},"from":{"type":"string","example":"09:00","description":"Local start of the interval, inclusive"},"to":{"type":"string","example":"18:00","description":"Earlier than `from` means the interval runs past midnight into the next day. 24:00 = end of day."}},"required":["days","from","to"]},"WorkScheduleDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"False keeps the agent working round the clock, without losing the intervals"},"intervals":{"description":"The agent works during the union of these intervals; an empty list means round the clock","type":"array","items":{"$ref":"#/components/schemas/WorkScheduleIntervalDto"}}},"required":["enabled","intervals"]},"AssistantNewResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Assistant id"},"companyId":{"type":"string","format":"uuid","description":"Company the assistant belongs to"},"name":{"type":"string","description":"Display name","example":"Sales Assistant"},"firstMessage":{"type":"string","description":"Greeting the assistant says first when a call starts"},"promptText":{"type":"string","description":"System prompt"},"language":{"type":"string","description":"Primary conversation language (ISO 639-1)","example":"uk"},"timezone":{"type":"string","nullable":true,"description":"IANA timezone; null = platform default","example":"Europe/Kyiv"},"folderId":{"type":"string","format":"uuid","nullable":true,"description":"Folder the assistant is in"},"textWorkSchedule":{"nullable":true,"description":"Hours the agent answers chats, in `timezone`; null or disabled = round the clock","type":"object","allOf":[{"$ref":"#/components/schemas/WorkScheduleDto"}]},"voiceWorkSchedule":{"nullable":true,"description":"Hours the agent takes and makes calls, in `timezone`; null or disabled = round the clock","type":"object","allOf":[{"$ref":"#/components/schemas/WorkScheduleDto"}]},"assistantOutputAudioFormat":{"type":"string","enum":["pcm_8000","pcm_16000","pcm_22050","pcm_24000","pcm_44100","pcm_48000","ulaw_8000"],"description":"Audio format of the assistant's voice output"},"userInputAudioFormat":{"type":"string","enum":["pcm_8000","pcm_16000","pcm_22050","pcm_24000","pcm_44100","pcm_48000","ulaw_8000"],"description":"Audio format expected from the caller"},"isDefault":{"type":"boolean","description":"Whether this is the company's default assistant"},"eagerness":{"type":"string","enum":["eager","normal","patient"],"description":"How quickly the voice agent takes its turn"},"softTimeout":{"type":"number","description":"Seconds of silence before the soft-timeout message; -1 = disabled"},"softTimeoutMessage":{"type":"string","nullable":true,"description":"Phrase spoken when the soft timeout fires"},"streamingLatency":{"type":"number","description":"Voice streaming latency optimisation, 0–4"},"turnAfterSilence":{"type":"number","description":"Seconds of caller silence before the agent forces its turn; -1 = wait indefinitely"},"silenceEndCallTimeout":{"type":"number","description":"Seconds of silence before the call is ended; -1 = disabled"},"aiAutoReturnDelayMinutes":{"type":"number","description":"Minutes before the AI takes a chat back from a human operator; -1 = disabled"},"contextHistoryRatio":{"type":"object","nullable":true,"description":"Share of the model context window kept for verbatim dialog history; null = platform default"},"chatBehavior":{"type":"object","nullable":true,"description":"Chat behaviour: replyTiming (fixed|smart window), followUp (pings after the customer goes silent), multiMessage (several messages per reply). null = platform defaults"},"disableAiOnOwnerReply":{"type":"boolean","description":"When true, the chat owner's messages do not switch the chat to manual mode"},"disableFirstMessageInterruption":{"type":"boolean","description":"When true, the caller cannot interrupt the greeting"},"billTestsToCompany":{"type":"boolean","description":"Whether test runs of this assistant are billed to the company"},"activeLlmAssistantIntegrationId":{"type":"object","format":"uuid","nullable":true,"description":"Assistant LLM integration used for chats"},"parentAssistantId":{"type":"object","format":"uuid","nullable":true,"description":"Set only on draft variants: the assistant this draft belongs to. null for regular assistants."},"variantName":{"type":"object","nullable":true,"description":"Draft variants only: variant name"},"branchedFromVersionId":{"type":"object","format":"uuid","nullable":true,"description":"Draft variants only: version it was branched from"},"draftTextLlmModelId":{"type":"object","format":"uuid","nullable":true,"description":"Draft variants only: text-model override"},"draftBranchVersion":{"type":"number","description":"Draft variants only: number of voice snapshots pushed for testing"},"createdAt":{"format":"date-time","type":"string","description":"Creation time"},"updatedAt":{"format":"date-time","type":"string","description":"Last update time"}},"required":["id","companyId","assistantOutputAudioFormat","userInputAudioFormat","isDefault","eagerness","softTimeout","streamingLatency","turnAfterSilence","silenceEndCallTimeout","aiAutoReturnDelayMinutes","disableAiOnOwnerReply","disableFirstMessageInterruption","billTestsToCompany","createdAt","updatedAt"]},"AssistantNewListResponseDto":{"type":"object","properties":{"data":{"description":"Page of assistants, most recently updated first","type":"array","items":{"$ref":"#/components/schemas/AssistantNewResponseDto"}},"totalCount":{"type":"number","description":"Total number of assistants matching the filter","example":3}},"required":["data","totalCount"]},"LlmFallbackDto":{"type":"object","properties":{"provider":{"type":"string","example":"gemini","description":"Provider of the fallback text model"},"model":{"type":"string","example":"gemini-3.8-flash","description":"Fallback text model id"}}},"LlmDto":{"type":"object","properties":{"timeoutSec":{"type":"number","minimum":5,"maximum":120,"description":"Seconds one model call may take before it is abandoned"},"fallback":{"nullable":true,"description":"Model to answer with when the primary one fails; null = never fall back; absent = platform default","type":"object","allOf":[{"$ref":"#/components/schemas/LlmFallbackDto"}]}}},"ReplyTimingDto":{"type":"object","properties":{"mode":{"type":"string","enum":["fixed","smart"],"description":"fixed = wait burstWindowSec of silence, then answer. smart = short burst window, start generating right away, a classifier decides whether to hold for the rest of the thought; a newer message cancels the draft."},"burstWindowSec":{"type":"number","minimum":1,"maximum":60,"description":"Seconds of silence before generation starts"},"maxHoldSec":{"type":"number","minimum":0,"maximum":30,"description":"smart only: max seconds a finished draft waits for the customer to finish the thought"}}},"FollowUpActiveHoursDto":{"type":"object","properties":{"from":{"type":"string","example":"09:00","description":"HH:MM in the assistant's timezone"},"to":{"type":"string","example":"21:00","description":"HH:MM in the assistant's timezone"}}},"FollowUpDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Ping the customer who went silent (8/12/16/24 h by default)"},"delaysHours":{"example":[8,12,16,24],"description":"Hours after the customer's last message at which a follow-up is considered; fractions allowed","type":"array","items":{"type":"number"}},"maxPings":{"type":"number","minimum":1,"maximum":8,"description":"Max follow-ups per silence episode"},"instructions":{"type":"object","nullable":true,"description":"Extra guidance for the follow-up writer (tone, offers, taboos)"},"activeHours":{"nullable":true,"description":"Local-time send window","type":"object","allOf":[{"$ref":"#/components/schemas/FollowUpActiveHoursDto"}]}}},"MultiMessageDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Allow several messenger messages per reply (one per product/option)"},"maxParts":{"type":"number","minimum":2,"maximum":10,"description":"Upper bound of messages per reply"}}},"ChatBehaviorDto":{"type":"object","properties":{"llm":{"description":"Model timeout and fallback model for chat replies","allOf":[{"$ref":"#/components/schemas/LlmDto"}]},"replyTiming":{"description":"When the assistant starts answering: fixed quiet window or smart turn-taking","allOf":[{"$ref":"#/components/schemas/ReplyTimingDto"}]},"followUp":{"description":"Follow-up pings after the customer goes silent","allOf":[{"$ref":"#/components/schemas/FollowUpDto"}]},"multiMessage":{"description":"Splitting one reply into several messenger messages","allOf":[{"$ref":"#/components/schemas/MultiMessageDto"}]}}},"CreateVaRegistrationDto":{"type":"object","properties":{"providerType":{"type":"string","enum":["elevenlabs","gemini","openai","deepgram","xai","duplex","hybrid"],"description":"Voice engine that runs the assistant's calls","example":"gemini"},"voiceLlmModelId":{"type":"string","description":"Voice model id from the model catalog of the chosen provider. Omit to use the provider default."},"textLlmModelId":{"type":"string","description":"Text model id used for chat replies. Omit to use the platform default."},"companyIntegrationId":{"type":"string","format":"uuid","description":"Bring-your-own-key: id of the company's LLM integration whose credentials the provider should use"},"providerConfig":{"type":"object","description":"Provider-specific settings passed through to the voice engine as is (voice, TTS model…)","example":{"vaVoice":"Puck"}}},"required":["providerType"]},"CreateAssistantNewDto":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":80,"description":"Display name","example":"Sales Assistant"},"companyId":{"type":"string","format":"uuid","description":"Company the assistant belongs to. Must be the company of your Access Token."},"firstMessage":{"type":"string","description":"Greeting the assistant says first when a call starts","example":"Hi! How can I help you today?"},"promptText":{"type":"string","description":"System prompt: role, tone, rules and facts the assistant works with","example":"You are a helpful sales assistant for Acme Corp. Answer questions about our products."},"language":{"type":"string","description":"Primary conversation language (ISO 639-1)","example":"uk"},"timezone":{"type":"string","example":"Europe/Kyiv","description":"IANA timezone the agent reasons in"},"folderId":{"type":"string","format":"uuid","description":"Folder to put the assistant in"},"assistantOutputAudioFormat":{"type":"string","enum":["pcm_8000","pcm_16000","pcm_22050","pcm_24000","pcm_44100","pcm_48000","ulaw_8000"],"description":"Audio format of the assistant's voice output on calls"},"userInputAudioFormat":{"type":"string","enum":["pcm_8000","pcm_16000","pcm_22050","pcm_24000","pcm_44100","pcm_48000","ulaw_8000"],"description":"Audio format expected from the caller on calls"},"eagerness":{"type":"string","enum":["eager","normal","patient"],"default":"normal","description":"How quickly the voice agent takes its turn after the caller stops speaking"},"isDefault":{"type":"boolean","default":false,"description":"Mark as the company's default assistant"},"softTimeout":{"type":"number","default":-1,"description":"Seconds of caller silence before the soft-timeout message is spoken; -1 disables it"},"softTimeoutMessage":{"type":"string","description":"Phrase spoken when the soft timeout fires","example":"Are you still there?"},"streamingLatency":{"type":"number","default":2,"minimum":0,"maximum":4,"description":"Voice streaming latency optimisation, 0 (best quality) to 4 (lowest latency)"},"turnAfterSilence":{"type":"number","default":7,"description":"Seconds since the caller last spoke after which the agent forces its turn; -1 waits indefinitely"},"silenceEndCallTimeout":{"type":"number","default":-1,"description":"Seconds of silence after which the call is ended automatically; -1 disables it"},"aiAutoReturnDelayMinutes":{"type":"number","default":30,"description":"Minutes after a human operator's last reply before the AI takes the chat back automatically; -1 disables the auto-return"},"contextHistoryRatio":{"type":"object","description":"Share of the model context window reserved for the verbatim dialog history (0.05-0.9). Null = platform default. Lower values cost less per reply but fold older turns into the running summary sooner.","minimum":0.05,"maximum":0.9},"disableAiOnOwnerReply":{"type":"boolean","default":false,"description":"When true, a message sent by the chat owner does not switch the chat to manual mode — the AI keeps answering"},"chatBehavior":{"nullable":true,"description":"Chat behaviour: reply timing (fixed window vs smart turn-taking), follow-up pings after the customer goes silent, multi-message replies. null resets to platform defaults.","type":"object","allOf":[{"$ref":"#/components/schemas/ChatBehaviorDto"}]},"disableFirstMessageInterruption":{"type":"boolean","default":false,"description":"When true, the caller cannot interrupt the greeting"},"vaRegistration":{"description":"Voice engine and model settings","allOf":[{"$ref":"#/components/schemas/CreateVaRegistrationDto"}]}},"required":["name","companyId","vaRegistration"]},"AssistantNewCreatedResponseDto":{"type":"object","properties":{"assistant":{"description":"The created assistant","allOf":[{"$ref":"#/components/schemas/AssistantNewResponseDto"}]}},"required":["assistant"]},"UpdateVaRegistrationDto":{"type":"object","properties":{"providerType":{"type":"string","enum":["elevenlabs","gemini","openai","deepgram","xai","duplex","hybrid"],"description":"Switch the voice engine. Settings you don't resend (voice model, providerConfig) are reset for the new engine."},"providerId":{"type":"string","format":"uuid","nullable":true,"description":"Pin a specific provider (public or your company's own). providerType is derived from it; null resets to the default provider for providerType."},"voiceLlmModelId":{"type":"string","nullable":true,"description":"Voice model id from the provider's model catalog; null resets to the provider default","example":"gemini-live-2.5-flash-preview"},"textLlmModelId":{"type":"string","description":"Text model id used for chat replies"},"companyIntegrationId":{"type":"string","format":"uuid","nullable":true,"description":"Bring-your-own-key: company LLM integration whose credentials to use; null removes it"},"providerConfig":{"type":"object","description":"Provider-specific settings passed through to the voice engine as is (voice, TTS model…)","example":{"vaVoice":"Puck"}}}},"UpdateAssistantNewDto":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":80,"description":"Display name","example":"Sales Assistant"},"firstMessage":{"type":"string","description":"Greeting the assistant says first when a call starts","example":"Hi! How can I help you today?"},"promptText":{"type":"string","description":"System prompt: role, tone, rules and facts the assistant works with"},"language":{"type":"string","description":"Primary conversation language (ISO 639-1)","example":"uk"},"timezone":{"type":"string","example":"Europe/Kyiv","description":"IANA timezone the agent reasons in"},"textWorkSchedule":{"nullable":true,"description":"Hours the agent answers chats, in the assistant's timezone. null / disabled = round the clock.","type":"object","allOf":[{"$ref":"#/components/schemas/WorkScheduleDto"}]},"voiceWorkSchedule":{"nullable":true,"description":"Hours the agent takes and makes calls, in the assistant's timezone. null / disabled = round the clock.","type":"object","allOf":[{"$ref":"#/components/schemas/WorkScheduleDto"}]},"disableFirstMessageInterruption":{"type":"boolean","description":"When true, the caller cannot interrupt the greeting"},"eagerness":{"type":"string","enum":["eager","normal","patient"],"description":"How quickly the voice agent takes its turn after the caller stops speaking"},"softTimeout":{"type":"number","description":"Seconds of caller silence before the soft-timeout message is spoken; -1 disables it"},"softTimeoutMessage":{"type":"string","description":"Phrase spoken when the soft timeout fires","example":"Are you still there?"},"streamingLatency":{"type":"number","minimum":0,"maximum":4,"description":"Voice streaming latency optimisation, 0 (best quality) to 4 (lowest latency)"},"turnAfterSilence":{"type":"number","description":"Seconds since the caller last spoke after which the agent forces its turn; -1 waits indefinitely"},"silenceEndCallTimeout":{"type":"number","description":"Seconds of silence after which the call is ended automatically; -1 disables it"},"aiAutoReturnDelayMinutes":{"type":"number","description":"Minutes after a human operator's last reply before the AI takes the chat back automatically; -1 disables the auto-return"},"contextHistoryRatio":{"type":"object","description":"Share of the model context window reserved for the verbatim dialog history (0.05-0.9). Null = platform default. Lower values cost less per reply but fold older turns into the running summary sooner.","minimum":0.05,"maximum":0.9},"disableAiOnOwnerReply":{"type":"boolean","description":"When true, an outgoing message from the chat owner does NOT switch the chat into manual mode (AI keeps running). Default false preserves the existing takeover behavior."},"chatBehavior":{"nullable":true,"description":"Chat behaviour: reply timing (fixed window vs smart turn-taking), follow-up pings after the customer goes silent, multi-message replies. null resets to platform defaults.","type":"object","allOf":[{"$ref":"#/components/schemas/ChatBehaviorDto"}]},"activeLlmAssistantIntegrationId":{"type":"string","format":"uuid","nullable":true,"description":"Assistant LLM integration to answer chats with; null clears it"},"draftTextLlmModelId":{"type":"string","format":"uuid","nullable":true,"description":"Draft variants only: text-model override; ignored on regular assistants. null resets it."},"vaRegistration":{"description":"Voice engine and model settings. Changing them re-provisions the voice agent.","allOf":[{"$ref":"#/components/schemas/UpdateVaRegistrationDto"}]}}},"AssistantNewDeleteResponseDto":{"type":"object","properties":{"deleted":{"type":"boolean","description":"true — the assistant is gone. false — deletion was accepted and finishes in the background."},"terminating":{"type":"boolean","description":"Present when deleted is false: the voice agent is being torn down; the assistant disappears once that completes"}},"required":["deleted"]},"CopyAssistantNewDto":{"type":"object","properties":{"vaRegistration":{"description":"Voice engine settings for the copy. Omit to keep the source assistant's engine.","allOf":[{"$ref":"#/components/schemas/UpdateVaRegistrationDto"}]}}},"ParameterDto":{"type":"object","properties":{"name":{"type":"string","description":"Parameter name as passed to the tool","example":"customerName"},"type":{"type":"string","enum":["string","number","boolean","array","object"],"description":"Parameter data type","example":"string"},"isRequired":{"type":"boolean","description":"Whether the parameter is required for the tool call","example":true},"value_type":{"type":"string","enum":["llm_prompt","constant","dynamic_variable"],"description":"How the value is resolved: llm_prompt — extracted by the LLM, constant — fixed value, dynamic_variable — taken from call variables","example":"llm_prompt"},"description":{"type":"string","description":"Explanation of the parameter for the LLM","example":"Name of the customer"},"constant_value":{"type":"object","description":"Fixed value used when value_type is constant","example":"en"},"dynamic_variable":{"type":"string","description":"Name of the dynamic variable used when value_type is dynamic_variable","example":"customer_name"},"dynamic_variable_value":{"type":"string","description":"Fallback value for the dynamic variable when it is not set","example":"Guest"},"items":{"description":"Schema of array items when type is array","allOf":[{"$ref":"#/components/schemas/ParameterDto"}]},"properties":{"description":"Nested object properties when type is object","type":"array","items":{"$ref":"#/components/schemas/ParameterDto"}}},"required":["name","type","isRequired"]},"AssistantToolResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Tool ID","format":"uuid","example":"3f6c2a1e-8b7d-4c5e-9a10-2b3c4d5e6f70"},"name":{"type":"string","description":"Function name the LLM calls","example":"check_order_status"},"description":{"type":"object","description":"What the tool does and when the assistant should call it","nullable":true,"example":"Returns the delivery status of an order by its number"},"type":{"type":"string","description":"Tool type","enum":["webhook_init","webhook_action","webhook_postcall","webhook_client","system"],"example":"webhook_action"},"apiCallUrl":{"type":"object","description":"Your endpoint Happ calls","nullable":true,"example":"https://example.com/hooks/order-status"},"httpMethod":{"type":"string","description":"HTTP method used to call apiCallUrl","enum":["get","post","put","delete","patch"],"nullable":true,"example":"post"},"executionMode":{"type":"string","description":"When the call runs relative to the assistant's speech (voice calls)","enum":["immediate","post_tool_speech","async"],"example":"immediate"},"disableInterruptions":{"type":"boolean","description":"Caller can't interrupt the assistant while the tool runs (voice)","example":false},"toolCallSound":{"type":"string","description":"Sound played to the caller while the tool runs (voice)","enum":["elevator1","elevator2","elevator3","elevator4","typing"],"nullable":true},"params":{"description":"Arguments the assistant fills when calling the tool","nullable":true,"type":"array","items":{"$ref":"#/components/schemas/ParameterDto"}},"assistantId":{"type":"string","description":"Assistant the tool belongs to","format":"uuid"},"isSystem":{"type":"boolean","description":"Created by Happ (CRM integrations, post-call). System tools can't be deleted","example":false},"disabled":{"type":"boolean","description":"Tool is switched off and not offered to the assistant","example":false},"createdAt":{"format":"date-time","type":"string","description":"Creation time"},"updatedAt":{"format":"date-time","type":"string","description":"Last update time"}},"required":["id","name","type","executionMode","disableInterruptions","assistantId","isSystem","disabled","createdAt","updatedAt"]},"AssistantToolListResponseDto":{"type":"object","properties":{"data":{"description":"Page of tools, newest first","type":"array","items":{"$ref":"#/components/schemas/AssistantToolResponseDto"}},"totalCount":{"type":"number","description":"Total number of tools matching the filter","example":3}},"required":["data","totalCount"]},"AssistantToolCallResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"ID of the function-call message","format":"uuid"},"chatId":{"type":"string","description":"Chat (conversation) where the assistant called the tool","format":"uuid"},"functionCallId":{"type":"object","description":"Model-side call id linking the call to its output","nullable":true,"example":"call_9xQ2"},"name":{"type":"string","description":"Tool name","example":"check_order_status"},"input":{"type":"object","description":"Arguments sent, as a JSON string","nullable":true,"example":"{\"orderNumber\":\"10423\"}"},"output":{"type":"object","description":"Your endpoint's response, as a JSON string; null while the call has not returned","nullable":true,"example":"{\"status\":\"shipped\"}"},"success":{"type":"object","description":"false when the output contains `\"error\"` or `\"success\": false`; null while the call has not returned","nullable":true,"example":true},"elapsedMs":{"type":"object","description":"Call duration in milliseconds","nullable":true,"example":184},"createdAt":{"format":"date-time","type":"string","description":"When the assistant called the tool"}},"required":["id","chatId","name","createdAt"]},"AssistantToolCallStatsResponseDto":{"type":"object","properties":{"totalCalls":{"type":"number","description":"Total calls of the tool","example":120},"errorCalls":{"type":"number","description":"Calls that returned an error","example":3},"errorRate":{"type":"number","description":"errorCalls / completed calls, 0..1","example":0.025},"avgLatencyMs":{"type":"object","description":"Average call duration in milliseconds","nullable":true,"example":210}},"required":["totalCalls","errorCalls","errorRate"]},"AssistantToolCallListResponseDto":{"type":"object","properties":{"data":{"description":"Page of calls, newest first","type":"array","items":{"$ref":"#/components/schemas/AssistantToolCallResponseDto"}},"totalCount":{"type":"number","description":"Total number of calls","example":120},"stats":{"description":"Aggregates over recent calls","allOf":[{"$ref":"#/components/schemas/AssistantToolCallStatsResponseDto"}]}},"required":["data","totalCount","stats"]},"CreateAssistantToolNewDto":{"type":"object","properties":{"name":{"type":"string","description":"Function name the LLM calls; use snake_case, unique per assistant","example":"check_order_status"},"description":{"type":"string","description":"What the tool does and when the assistant should call it — the model decides from this text","example":"Returns the delivery status of an order. Call it when the customer asks where their order is."},"type":{"type":"string","enum":["webhook_init","webhook_action","webhook_postcall","webhook_client","system"],"default":"webhook_action","description":"webhook_action — called during a conversation; webhook_init — voice, at call start, returns dynamic variables; webhook_postcall — voice, after the call; webhook_client — executed by the client app. system is reserved for Happ"},"apiCallUrl":{"type":"string","description":"Your endpoint Happ calls (required for webhook types except webhook_client)","example":"https://example.com/hooks/order-status"},"httpMethod":{"type":"string","enum":["get","post","put","delete","patch"],"description":"HTTP method for apiCallUrl. POST/PUT/PATCH send arguments as a JSON body, GET/DELETE as query parameters","example":"post"},"executionMode":{"type":"string","enum":["immediate","post_tool_speech","async"],"default":"immediate","description":"Voice: immediate — run right away; post_tool_speech — after the assistant finishes its phrase; async — in the background without waiting for the result"},"disableInterruptions":{"type":"boolean","default":false,"description":"Voice: the caller can't interrupt the assistant while the tool runs"},"disabled":{"type":"boolean","default":false,"description":"Switch the tool off without deleting it"},"webhookSecret":{"type":"string","description":"Shared secret: Happ signs the request body with HMAC-SHA256 (X-Signature header). Write-only, never returned","example":"whsec_9f8e7d6c5b4a3210"},"toolCallSound":{"type":"string","enum":["elevator1","elevator2","elevator3","elevator4","typing"],"description":"Voice: sound played to the caller while the tool runs"},"params":{"description":"Arguments the assistant fills when calling the tool","type":"array","items":{"$ref":"#/components/schemas/ParameterDto"}},"assistantId":{"type":"string","format":"uuid","description":"Assistant the tool belongs to"}},"required":["name","assistantId"]},"UpdateAssistantToolNewDto":{"type":"object","properties":{"name":{"type":"string","description":"Function name the LLM calls; use snake_case, unique per assistant","example":"check_order_status"},"description":{"type":"string","description":"What the tool does and when the assistant should call it — the model decides from this text","example":"Returns the delivery status of an order. Call it when the customer asks where their order is."},"type":{"type":"string","enum":["webhook_init","webhook_action","webhook_postcall","webhook_client","system"],"description":"webhook_action — called during a conversation; webhook_init — voice, at call start, returns dynamic variables; webhook_postcall — voice, after the call; webhook_client — executed by the client app. system is reserved for Happ"},"apiCallUrl":{"type":"string","description":"Your endpoint Happ calls (required for webhook types except webhook_client)","example":"https://example.com/hooks/order-status"},"httpMethod":{"type":"string","enum":["get","post","put","delete","patch"],"description":"HTTP method for apiCallUrl. POST/PUT/PATCH send arguments as a JSON body, GET/DELETE as query parameters","example":"post"},"executionMode":{"type":"string","enum":["immediate","post_tool_speech","async"],"description":"Voice: immediate — run right away; post_tool_speech — after the assistant finishes its phrase; async — in the background without waiting for the result"},"disableInterruptions":{"type":"boolean","description":"Voice: the caller can't interrupt the assistant while the tool runs"},"disabled":{"type":"boolean","description":"Switch the tool off without deleting it"},"webhookSecret":{"type":"string","description":"Shared secret: Happ signs the request body with HMAC-SHA256 (X-Signature header). Write-only, never returned","example":"whsec_9f8e7d6c5b4a3210"},"toolCallSound":{"type":"string","enum":["elevator1","elevator2","elevator3","elevator4","typing"],"description":"Voice: sound played to the caller while the tool runs"},"params":{"description":"Arguments the assistant fills when calling the tool","type":"array","items":{"$ref":"#/components/schemas/ParameterDto"}}}},"AssistantToolDeletedResponseDto":{"type":"object","properties":{"deleted":{"type":"boolean","description":"Always true; deleting a missing item also succeeds","example":true}},"required":["deleted"]},"KnowledgeFileResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"File ID","format":"uuid"},"name":{"type":"string","description":"Original file name","example":"price-list.pdf"},"url":{"type":"string","description":"Download URL","example":"https://files.happ.tools/price-list.pdf"},"mimeType":{"type":"string","description":"MIME type","example":"application/pdf"},"size":{"type":"number","description":"Size in bytes","example":48213}},"required":["id","name","url"]},"KnowledgeResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Knowledge item ID","format":"uuid","example":"7b1d0c2e-4f3a-4e8b-9c6d-1a2b3c4d5e6f"},"name":{"type":"string","description":"Display name","example":"Delivery and returns"},"type":{"type":"string","description":"Source type","enum":["url","text","file"],"example":"text"},"resourceUrl":{"type":"object","description":"Source URL (type url), or the file's download URL (type file)","nullable":true,"example":"https://example.com/delivery"},"resourceDescription":{"type":"object","description":"Short description of what the item covers","nullable":true,"example":"Shipping times, costs and the return policy"},"resourceContent":{"type":"object","description":"Text content (typed in, or extracted from the file)","nullable":true,"example":"We ship across Ukraine in 1-3 days..."},"fileId":{"type":"object","description":"Attached file ID (type file)","format":"uuid","nullable":true},"file":{"description":"Attached file (type file)","nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/KnowledgeFileResponseDto"}]},"isVector":{"type":"boolean","description":"Indexed for semantic search (always true for type url)","example":false},"indexStatus":{"type":"string","description":"Indexing status for searchable items: pending — being chunked and indexed in the background, indexed — searchable, failed — indexing failed (fix the source and update the item)","enum":["pending","indexed","failed"],"nullable":true,"example":"pending"},"indexedAt":{"type":"object","description":"When indexing last finished","format":"date-time","nullable":true},"chunkingStrategy":{"type":"string","description":"How the content is split into chunks","enum":["tokens","items"],"example":"tokens"},"itemDelimiter":{"type":"object","description":"Separator between items when chunkingStrategy is items","nullable":true,"example":"---"},"extraChunkPayloads":{"type":"array","description":"Extra payload merged into each chunk, by chunk index","items":{"type":"object","additionalProperties":true},"nullable":true},"assistantId":{"type":"string","description":"Assistant the item belongs to","format":"uuid"},"isSystem":{"type":"boolean","description":"Managed by Happ (e.g. a synced CRM catalog)","example":false},"vlmCatalogId":{"type":"object","description":"Synced CRM catalog ID, when the item mirrors one","nullable":true},"vlmLastJobId":{"type":"object","description":"Last catalog/URL processing job ID","nullable":true},"createdAt":{"format":"date-time","type":"string","description":"Creation time"},"updatedAt":{"format":"date-time","type":"string","description":"Last update time"}},"required":["id","name","type","isVector","chunkingStrategy","assistantId","isSystem","createdAt","updatedAt"]},"KnowledgeListResponseDto":{"type":"object","properties":{"data":{"description":"Page of knowledge items","type":"array","items":{"$ref":"#/components/schemas/KnowledgeResponseDto"}},"totalCount":{"type":"number","description":"Total number of items matching the filter","example":12}},"required":["data","totalCount"]},"CreateAssistantKnowledgeNewDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name of the item","example":"Delivery and returns"},"type":{"type":"string","enum":["url","text","file"],"description":"Source type: text — resourceContent; url — resourceUrl is crawled; file — fileId of an uploaded file"},"resourceUrl":{"type":"string","description":"Resource URL (required when type is url)"},"resourceDescription":{"type":"string","description":"Short description of what the item covers","example":"Shipping times, costs and the return policy"},"resourceContent":{"type":"string","description":"Text content (type text)","example":"We ship across Ukraine in 1-3 days..."},"fileId":{"type":"string","description":"File id (its text is extracted into resource_content)"},"isVector":{"type":"boolean","default":false,"description":"false — content is given to the assistant as is (short texts); true — chunked, indexed in the background and searched (large documents, catalogs). Always true for type url"},"chunkingStrategy":{"type":"string","enum":["tokens","items"],"default":"tokens","description":"How content is split for search: tokens — fixed-size chunks; items — split by itemDelimiter (one chunk per product/FAQ entry)"},"itemDelimiter":{"type":"string","description":"Separator between items when chunkingStrategy is items","example":"---"},"extraChunkPayloads":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Advanced: extra payload merged into each chunk, by chunk index"},"assistantId":{"type":"string","format":"uuid","description":"Assistant the item belongs to"}},"required":["name","type","assistantId"]},"UpdateAssistantKnowledgeNewDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name of the item","example":"Delivery and returns"},"type":{"type":"string","enum":["url","text","file"],"description":"Source type: text — resourceContent; url — resourceUrl is crawled; file — fileId of an uploaded file"},"resourceUrl":{"type":"string","description":"Page URL (required for type url)","example":"https://example.com/delivery"},"resourceDescription":{"type":"string","description":"Short description of what the item covers","example":"Shipping times, costs and the return policy"},"resourceContent":{"type":"string","description":"Text content (type text)","example":"We ship across Ukraine in 1-3 days..."},"fileId":{"type":"string","description":"ID of an uploaded file (type file); its text is extracted into resourceContent"},"isVector":{"type":"boolean","description":"false — content is given to the assistant as is (short texts); true — chunked, indexed in the background and searched (large documents, catalogs). Always true for type url"},"chunkingStrategy":{"type":"string","enum":["tokens","items"],"description":"How content is split for search: tokens — fixed-size chunks; items — split by itemDelimiter (one chunk per product/FAQ entry)"},"itemDelimiter":{"type":"string","description":"Separator between items when chunkingStrategy is items","example":"---"},"extraChunkPayloads":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Advanced: extra payload merged into each chunk, by chunk index"}}},"KnowledgeDeletedResponseDto":{"type":"object","properties":{"deleted":{"type":"boolean","description":"Always true; deleting a missing item also succeeds","example":true}},"required":["deleted"]},"OriginateCallDto":{"type":"object","properties":{"phoneNumber":{"type":"string","description":"Phone number to originate call to. Accepts +<digits>, plain digits with country code (e.g. 380...), or local format starting with 0.","example":"+380501234567"},"callerAssistantId":{"type":"string","description":"ID of the assistant to make the call as. Must belong to the same company as the assistant on the route. If omitted, the route assistant is used.","example":"00000000-0000-0000-0000-000000000000"},"dynamicVariables":{"type":"object","description":"Arbitrary key/value pairs forwarded to the voice assistant as `dynamic_variables`. Used to personalize the conversation (customer name, order id, preferred language, etc.).","additionalProperties":true,"example":{"customer_name":"John Doe","order_id":"ORD-12345","preferred_language":"en"}}},"required":["phoneNumber"]},"CallAssistantPhoneResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Phone number id"},"phoneNumber":{"type":"string","description":"The assistant's phone number used for the call","example":"+380441234567"}}},"CallTranscriptItemResponseDto":{"type":"object","properties":{"speaker":{"type":"string","description":"Who spoke: `agent` (the assistant) or `user` (the customer)","example":"agent"},"text":{"type":"string","description":"What was said","example":"Hello! This is Happ Store, how can I help?"}}},"CallReplyResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Reply id"},"speaker":{"type":"string","enum":["manager","client"],"description":"`manager` — the assistant or an employee, `client` — the customer"},"text":{"type":"string","description":"Reply text","example":"I'd like to check my order status"},"orderIndex":{"type":"number","description":"Position of the reply in the conversation, from 0","example":1},"startTimeSecs":{"type":"object","nullable":true,"description":"Seconds from call start when the reply began","example":4.2},"endTimeSecs":{"type":"object","nullable":true,"description":"Seconds from call start when the reply ended","example":7.9}},"required":["id","speaker","text","orderIndex"]},"CallDetailResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Call id"},"companyId":{"type":"string","format":"uuid","description":"Company id"},"assistantId":{"type":"object","format":"uuid","nullable":true,"description":"Assistant that handled the call; null for calls without an AI assistant (e.g. telephony-only)"},"direction":{"type":"object","nullable":true,"description":"`OUT` — outbound (e.g. started via Start an outbound call), `IN` — inbound, `INNER` — internal, `UNKNOWN`. Records imported from telephony may carry the provider's raw value","example":"OUT"},"status":{"type":"string","enum":["new","pending","transcribed","skipped","failed","rejected","not_dialed"],"description":"`transcribed` — call finished and the transcript is ready; `new`/`pending` — transcript is still being processed; `rejected` — the callee didn't answer or hung up before pickup; `not_dialed` — the dial queue expired before a line was free, nothing was dialed; `failed`/`skipped` — the call or its processing failed","example":"transcribed"},"source":{"type":"string","enum":["eleven_labs","voice_assistant","va","phonet","binotel","unitalk","ringostat","custom_calls","streamtele"],"nullable":true,"description":"Where the call record came from: `va` — Happ voice assistant, others — connected telephony"},"clientPhone":{"type":"object","nullable":true,"description":"Customer phone number","example":"380501234567"},"assistantPhone":{"nullable":true,"description":"The assistant's phone line used for the call","type":"object","allOf":[{"$ref":"#/components/schemas/CallAssistantPhoneResponseDto"}]},"callDate":{"type":"object","format":"date-time","nullable":true,"description":"When the call took place"},"duration":{"type":"number","description":"Call duration in seconds","example":95},"recordingUrl":{"type":"object","nullable":true,"description":"URL of the call recording (audio)"},"transcriptText":{"type":"object","nullable":true,"description":"Whole transcript as plain text, one `Agent:`/`User:` line per turn","example":"Agent: Hello! How can I help?\nUser: Where is my order?"},"transcription":{"nullable":true,"description":"Transcript turns as reported by the voice engine","type":"array","items":{"$ref":"#/components/schemas/CallTranscriptItemResponseDto"}},"callSuccessful":{"type":"object","nullable":true,"description":"Voice engine's evaluation of whether the call reached its goal: `success`, `failure` or `unknown`","example":"success"},"terminationReason":{"type":"object","nullable":true,"description":"Why the call ended","example":"Call ended by remote party"},"callError":{"type":"object","nullable":true,"description":"Error reported for the call, if any"},"language":{"type":"object","nullable":true,"description":"Detected conversation language","example":"uk"},"dynamicVariables":{"type":"object","additionalProperties":true,"nullable":true,"description":"Dynamic variables the call ran with — the `dynamicVariables` passed when starting the call plus system ones such as `phone_number`","example":{"order_id":"ORD-12345","phone_number":"+380501234567"}},"toolCalls":{"type":"array","items":{"type":"object","additionalProperties":true},"nullable":true,"description":"Assistant tool calls made during the call: `name`, `params`, `result`, `timeInCallSecs`"},"externalId":{"type":"object","nullable":true,"description":"Id of the call at the voice engine or telephony provider"},"managerId":{"type":"object","format":"uuid","nullable":true,"description":"Employee the call is assigned to"},"employeeName":{"type":"object","nullable":true,"description":"Employee name reported by telephony, for calls handled by staff"},"createdAt":{"type":"string","format":"date-time","description":"When the record was created"},"updatedAt":{"type":"string","format":"date-time","description":"When the record was last updated"},"replies":{"description":"Transcript split into replies, in conversation order","type":"array","items":{"$ref":"#/components/schemas/CallReplyResponseDto"}}},"required":["id","companyId","status","duration","createdAt","updatedAt","replies"]},"CallResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Call id"},"companyId":{"type":"string","format":"uuid","description":"Company id"},"assistantId":{"type":"object","format":"uuid","nullable":true,"description":"Assistant that handled the call; null for calls without an AI assistant (e.g. telephony-only)"},"direction":{"type":"object","nullable":true,"description":"`OUT` — outbound (e.g. started via Start an outbound call), `IN` — inbound, `INNER` — internal, `UNKNOWN`. Records imported from telephony may carry the provider's raw value","example":"OUT"},"status":{"type":"string","enum":["new","pending","transcribed","skipped","failed","rejected","not_dialed"],"description":"`transcribed` — call finished and the transcript is ready; `new`/`pending` — transcript is still being processed; `rejected` — the callee didn't answer or hung up before pickup; `not_dialed` — the dial queue expired before a line was free, nothing was dialed; `failed`/`skipped` — the call or its processing failed","example":"transcribed"},"source":{"type":"string","enum":["eleven_labs","voice_assistant","va","phonet","binotel","unitalk","ringostat","custom_calls","streamtele"],"nullable":true,"description":"Where the call record came from: `va` — Happ voice assistant, others — connected telephony"},"clientPhone":{"type":"object","nullable":true,"description":"Customer phone number","example":"380501234567"},"assistantPhone":{"nullable":true,"description":"The assistant's phone line used for the call","type":"object","allOf":[{"$ref":"#/components/schemas/CallAssistantPhoneResponseDto"}]},"callDate":{"type":"object","format":"date-time","nullable":true,"description":"When the call took place"},"duration":{"type":"number","description":"Call duration in seconds","example":95},"recordingUrl":{"type":"object","nullable":true,"description":"URL of the call recording (audio)"},"transcriptText":{"type":"object","nullable":true,"description":"Whole transcript as plain text, one `Agent:`/`User:` line per turn","example":"Agent: Hello! How can I help?\nUser: Where is my order?"},"transcription":{"nullable":true,"description":"Transcript turns as reported by the voice engine","type":"array","items":{"$ref":"#/components/schemas/CallTranscriptItemResponseDto"}},"callSuccessful":{"type":"object","nullable":true,"description":"Voice engine's evaluation of whether the call reached its goal: `success`, `failure` or `unknown`","example":"success"},"terminationReason":{"type":"object","nullable":true,"description":"Why the call ended","example":"Call ended by remote party"},"callError":{"type":"object","nullable":true,"description":"Error reported for the call, if any"},"language":{"type":"object","nullable":true,"description":"Detected conversation language","example":"uk"},"dynamicVariables":{"type":"object","additionalProperties":true,"nullable":true,"description":"Dynamic variables the call ran with — the `dynamicVariables` passed when starting the call plus system ones such as `phone_number`","example":{"order_id":"ORD-12345","phone_number":"+380501234567"}},"toolCalls":{"type":"array","items":{"type":"object","additionalProperties":true},"nullable":true,"description":"Assistant tool calls made during the call: `name`, `params`, `result`, `timeInCallSecs`"},"externalId":{"type":"object","nullable":true,"description":"Id of the call at the voice engine or telephony provider"},"managerId":{"type":"object","format":"uuid","nullable":true,"description":"Employee the call is assigned to"},"employeeName":{"type":"object","nullable":true,"description":"Employee name reported by telephony, for calls handled by staff"},"createdAt":{"type":"string","format":"date-time","description":"When the record was created"},"updatedAt":{"type":"string","format":"date-time","description":"When the record was last updated"}},"required":["id","companyId","status","duration","createdAt","updatedAt"]},"CallListResponseDto":{"type":"object","properties":{"data":{"description":"Calls, newest first by default","type":"array","items":{"$ref":"#/components/schemas/CallResponseDto"}},"totalCount":{"type":"number","description":"Total number of calls matching the query","example":128},"page":{"type":"number","description":"Page number derived from `skip` / `take`","example":1}},"required":["data","totalCount","page"]},"ConnectCallWebhookDto":{"type":"object","properties":{"webhookUrl":{"type":"string","description":"Public URL on your server that receives call events (use HTTPS)","example":"https://example.com/happ/calls"},"webhookSecret":{"type":"string","description":"Shared secret used to sign every delivery (`X-Signature: sha256=<hmac>`). Strongly recommended — without it requests are unsigned. Omit on a repeated connect to keep the current one.","example":"whsec_3f9a1c7e2b"},"assistantIds":{"description":"Assistants whose calls are delivered to this URL. Calls are matched by assistant, so a webhook without assistants receives nothing. An assistant can report to one URL at a time — connecting it to a new URL moves it there.","example":["7c9e6679-7425-40de-944b-e07fc1f90ae7"],"type":"array","items":{"type":"string"}}},"required":["webhookUrl"]},"IntegrationKindResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Integration kind ID","example":"4f1c2d7e-8a9b-4c3d-9e0f-1a2b3c4d5e6f"},"name":{"type":"string","description":"Human-readable integration name","example":"Chat Webhook"},"type":{"type":"string","description":"Integration type","enum":["telegram","telegram_push_notifications","slack_push_notifications","instagram","viber_echat","custom_messenger","custom_calls","voice_assistant","eleven_labs","google_spreadsheets_notifications","binotel","ringostat","phonet","unitalk","facebook","messenger","keycrm","nethunt","odoo","salesdrive","bookon","smartcrm","altegio","whatsapp_wasender","whatsapp_business","whatsapp_echat","openai","groq","claude","gemini","qdrant","chat_webhook","call_webhook","telegram_bot","widget","mobile_push_notifications","email_notifications","instagram_comments","facebook_comments","sitniks","streamtele","prom","rozetka","rozetka_comments","olx","uspacy","roapp","keepincrm","creatio","zoho","directhub","mrcrm","onebox","sendpulse","bitrix24","lpcrm","lakmus","cleverstaff","gmail","google_calendar","shopify","resend","woocommerce","hubspot","pipedrive","kommo","nova_poshta","boulevard"],"example":"chat_webhook"},"category":{"type":"string","description":"Integration category","enum":["messengers","push-notifications","telephony","crm","llm_models","vector_databases","webhooks","voice_assistant","comments","delivery"],"example":"webhooks"}},"required":["id","name","type","category"]},"CompanyIntegrationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Connected integration ID — use it to disconnect","example":"9b2e4c1a-3d5f-4e6a-8b7c-0d1e2f3a4b5c"},"companyId":{"type":"string","description":"Company the integration belongs to","example":"550e8400-e29b-41d4-a716-446655440000"},"integrationId":{"type":"string","description":"Integration kind ID","example":"4f1c2d7e-8a9b-4c3d-9e0f-1a2b3c4d5e6f"},"integration":{"description":"Integration kind","allOf":[{"$ref":"#/components/schemas/IntegrationKindResponseDto"}]},"isActive":{"type":"boolean","description":"Whether events are being delivered","example":true},"config":{"type":"object","description":"Integration settings with secrets removed. For a chat webhook: `url` and, if set, `chatOwnerIds`. The signing secret is never returned.","additionalProperties":true,"example":{"url":"https://example.com/happ/webhook","chatOwnerIds":["123456789"]}},"lastErrorAt":{"type":"string","description":"When the last delivery error happened","format":"date-time","nullable":true},"lastErrorReason":{"type":"string","description":"Reason of the last delivery error","nullable":true},"consecutiveErrors":{"type":"number","description":"Delivery errors in a row since the last success","example":0},"createdAt":{"type":"string","description":"Creation time","format":"date-time","example":"2026-10-07T09:30:00.000Z"},"updatedAt":{"type":"string","description":"Last update time","format":"date-time","example":"2026-10-07T09:30:00.000Z"}},"required":["id","companyId","integrationId","integration","isActive","config","consecutiveErrors","createdAt","updatedAt"]},"MediaModel":{"type":"object","properties":{"id":{"type":"string","description":"Media ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"type":{"type":"string","description":"Media kind","enum":["photo","video","audio","file"],"example":"photo"},"mediaUrl":{"type":"string","description":"Download URL","example":"https://cdn.happ.tools/media/a1b2.jpg"},"previewUrl":{"type":"string","description":"Thumbnail URL (photos and videos)","example":"https://cdn.happ.tools/media/a1b2-preview.jpg"},"name":{"type":"string","description":"Original file name","example":"invoice.pdf"},"mimeType":{"type":"string","description":"MIME type","example":"image/jpeg"},"size":{"type":"number","description":"File size in bytes","example":204800},"duration":{"type":"number","description":"Duration in seconds (audio and video)","example":12.5},"width":{"type":"number","description":"Width in pixels (photos and videos)","example":1280},"height":{"type":"number","description":"Height in pixels (photos and videos)","example":720},"description":{"type":"string","description":"AI-generated description of the media content","example":"Photo of a red dress"},"status":{"type":"string","description":"Processing state of the upload","enum":["processing","success","failed"],"example":"success"}},"required":["id","mediaUrl","status"]},"ChatCommentModel":{"type":"object","properties":{"id":{"type":"string","description":"Comment ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"text":{"type":"string","description":"Comment text","example":"Called back, waiting for payment"},"authorId":{"type":"string","description":"ID of the user who wrote the comment. Absent for comments added with an Access Token.","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"createdAt":{"type":"string","description":"When the comment was added (ISO 8601)","example":"2026-10-07T09:15:00.000Z"}},"required":["id","text","createdAt"]},"ChatMetaModel":{"type":"object","properties":{"comments":{"description":"Internal comments left by the team","type":"array","items":{"$ref":"#/components/schemas/ChatCommentModel"}},"sourceChannel":{"type":"string","description":"Aggregator chats only (SendPulse, Sitniks): the channel the customer actually wrote from, e.g. `telegram`, `instagram`.","example":"instagram"},"attribution":{"type":"object","description":"Where the conversation came from: `channel`, `first` and `last` touch (UTM parameters, click id of the ad network, landing page) and Meta ad context in `ad`. Present only when the channel reported it.","additionalProperties":true,"example":{"channel":"instagram","first":{"utmSource":"meta","utmCampaign":"autumn_sale"}}}}},"ChatTagSummaryModel":{"type":"object","properties":{"id":{"type":"string","description":"Tag ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"name":{"type":"string","description":"Tag name","example":"vip"},"color":{"type":"string","description":"Hex color of the chip","example":"#6366f1"}},"required":["id","name","color"]},"ChatModel":{"type":"object","properties":{"id":{"type":"string","description":"Chat ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"companyId":{"type":"string","description":"Company the chat belongs to","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"messenger":{"type":"string","description":"Channel the conversation runs on","enum":["instagram","telegram","telegram-bot","messenger","whatsapp","whatsapp-business","viber-echat","whatsapp-echat","custom-messenger","widget","prom","rozetka","olx","directhub","sendpulse","sitniks","gmail","resend","playground"],"example":"telegram"},"chatType":{"type":"string","description":"`private` — one-to-one dialog; `group` / `channel` — multi-party, the AI never answers there","enum":["private","group","channel"],"example":"private"},"fromId":{"type":"object","description":"ID of your connected account (bot, business account, page) that the customer writes to","nullable":true,"example":"6340798777"},"fromName":{"type":"object","description":"Name of your connected account","nullable":true,"example":"Acme Shop"},"toId":{"type":"object","description":"Customer ID in the channel","nullable":true,"example":"123456789"},"toName":{"type":"object","description":"Customer display name","nullable":true,"example":"Olena Kovalenko"},"username":{"type":"object","description":"Customer handle in the channel, without `@`","nullable":true,"example":"olena_k"},"phoneNumber":{"type":"object","description":"Customer phone number, if the channel shares it","nullable":true,"example":"+380501234567"},"avatarUrl":{"type":"object","description":"Customer profile photo URL","nullable":true,"example":"https://cdn.happ.tools/avatars/a1b2.jpg"},"externalId":{"type":"object","description":"Conversation ID in the source channel or integration","nullable":true,"example":"chat-42-external"},"telegramId":{"type":"object","description":"Telegram user ID of the customer (Telegram chats)","nullable":true,"example":"123456789"},"instagramId":{"type":"object","description":"Instagram-scoped user ID of the customer (Instagram chats)","nullable":true,"example":"17841400000000000"},"assistantId":{"type":"object","description":"Assistant that answers in this chat","format":"uuid","nullable":true,"example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"isUnderAiControl":{"type":"boolean","description":"`true` — the assistant answers the customer; `false` — a human handles the chat","example":true},"aiDisableReason":{"type":"string","description":"Why the AI is off: `manual` — a person turned it off; `auto` — the assistant handed the chat to a human; `insufficient_balance` — the company ran out of credits (turns back on automatically after top-up).","enum":["manual","auto","insufficient_balance"],"nullable":true,"example":null},"managerId":{"type":"object","description":"Team member responsible for the chat","format":"uuid","nullable":true,"example":null},"managerAssignmentSource":{"type":"object","description":"How the manager got assigned (manual, automatic routing…)","nullable":true,"example":null},"contactId":{"type":"object","description":"CRM contact this customer is matched to","format":"uuid","nullable":true,"example":null},"lastMessageAt":{"type":"object","description":"Time of the latest message","format":"date-time","nullable":true,"example":"2026-10-07T09:15:00.000Z"},"unreadCount":{"type":"number","description":"Number of customer messages not read by the team","example":2},"lastReadAt":{"type":"object","description":"When the chat was last read","format":"date-time","nullable":true,"example":"2026-10-07T09:15:00.000Z"},"maxAvailableConversations":{"type":"number","description":"Message limit the AI may still spend in this chat","example":100},"meta":{"description":"Comments and origin of the conversation","allOf":[{"$ref":"#/components/schemas/ChatMetaModel"}]},"folderId":{"type":"object","description":"Folder the chat is filed into (company folders, see `/chat-folders`); `null` when unfiled","format":"uuid","nullable":true,"example":null},"tags":{"description":"Tags attached to the chat (company dictionary, see `/chat-tags`)","type":"array","items":{"$ref":"#/components/schemas/ChatTagSummaryModel"}},"groupAccountIds":{"description":"Group chats only: IDs of your connected accounts that can send into the group","example":["6340798777"],"type":"array","items":{"type":"string"}},"assistant":{"type":"object","description":"Assigned assistant, when requested with `include=assistant`","additionalProperties":true},"manager":{"type":"object","description":"Assigned manager (user), when requested with `include=manager`","additionalProperties":true},"createdAt":{"type":"string","description":"Creation time","format":"date-time","example":"2026-10-07T09:15:00.000Z"},"updatedAt":{"type":"string","description":"Last update time","format":"date-time","example":"2026-10-07T09:15:00.000Z"}},"required":["id","companyId","messenger","chatType","isUnderAiControl","unreadCount","maxAvailableConversations","meta","createdAt","updatedAt"]},"ChatListModel":{"type":"object","properties":{"data":{"description":"Chats on this page, most recent activity first","type":"array","items":{"$ref":"#/components/schemas/ChatModel"}},"totalCount":{"type":"number","description":"Total number of chats matching the filters","example":248},"page":{"type":"number","description":"Page number for offset (`skip`/`take`) pagination","example":1},"nextCursor":{"type":"object","description":"Pass as `cursor` to get the next page; `null` on the last page or with offset pagination","nullable":true,"example":"eyJsYXN0TWVzc2FnZUF0IjoiMjAyNi0xMC0wNyJ9"}},"required":["data","totalCount","page"]},"ChannelIntegrationModel":{"type":"object","properties":{"id":{"type":"string","description":"Integration catalog ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"name":{"type":"string","description":"Integration name","example":"Telegram"},"type":{"type":"string","description":"Integration type","example":"telegram"},"category":{"type":"string","description":"Integration category","example":"messengers"}},"required":["id","name","type","category"]},"ChannelConfigModel":{"type":"object","properties":{"externalId":{"type":"string","description":"Account ID in the channel — matches `fromId` of its chats","example":"6340798777"},"username":{"type":"string","description":"Account handle in the channel","example":"acme_shop_bot"},"name":{"type":"string","description":"Label you gave the connection, to tell several accounts of one type apart","example":"Main shop"},"botId":{"type":"string","description":"Bot ID (bot-based channels)","example":"8123456789"},"widgetId":{"type":"string","description":"Widget ID (web widget)","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"bots":{"type":"object","description":"Aggregators (SendPulse): bots behind this connection, keyed by bot ID","additionalProperties":true},"channelAccounts":{"type":"object","description":"Aggregators (Sitniks): accounts behind this connection, keyed by account ID","additionalProperties":true}}},"ChannelModel":{"type":"object","properties":{"id":{"type":"string","description":"Connection ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"companyId":{"type":"string","description":"Company the connection belongs to","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"isActive":{"type":"boolean","description":"Always `true` — only active connections are listed","example":true},"integration":{"description":"What kind of channel this is","allOf":[{"$ref":"#/components/schemas/ChannelIntegrationModel"}]},"config":{"description":"Channel identity only — credentials are never returned","allOf":[{"$ref":"#/components/schemas/ChannelConfigModel"}]},"createdAt":{"type":"string","description":"When the channel was connected","format":"date-time","example":"2026-10-07T09:15:00.000Z"}},"required":["id","companyId","isActive","integration","config","createdAt"]},"ChannelListModel":{"type":"object","properties":{"data":{"description":"Connected channels","type":"array","items":{"$ref":"#/components/schemas/ChannelModel"}},"totalCount":{"type":"number","description":"Number of connected channels","example":3},"page":{"type":"number","description":"Always 1 — the list is not paginated","example":1}},"required":["data","totalCount"]},"CreateChatDto":{"type":"object","properties":{"companyId":{"type":"string","description":"ID of the company the chat belongs to","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"assistantId":{"type":"string","description":"Assistant that should answer in the chat","format":"uuid","example":"b2c3d4e5-f6a7-8901-bcde-f23456789012"},"messenger":{"type":"string","enum":["instagram","telegram","telegram-bot","messenger","whatsapp","whatsapp-business","viber-echat","whatsapp-echat","custom-messenger","widget","prom","rozetka","olx","directhub","sendpulse","sitniks","gmail","resend","playground"],"description":"Messenger channel the chat runs on","example":"telegram"},"telegramId":{"type":"string","description":"Telegram user ID of the chat counterpart","example":"123456789"},"instagramId":{"type":"string","description":"Instagram user ID of the chat counterpart","example":"17841400000000000"},"fromName":{"type":"string","description":"Name of your connected account","example":"Acme Shop"},"fromId":{"type":"string","description":"ID of your connected account in the channel (`config.externalId` from GET /chats/messengers)","example":"6340798777"},"toName":{"type":"string","description":"Customer display name","example":"Olena Kovalenko"},"toId":{"type":"string","description":"Customer ID in the channel","example":"123456789"},"displayName":{"type":"string","description":"Not stored — the inbox shows `toName`. Kept for backward compatibility.","example":"John Doe"},"externalId":{"type":"string","description":"External chat ID in the source messenger or integration","example":"chat-42-external"}},"required":["companyId","messenger"]},"UpdateChatDto":{"type":"object","properties":{"assistantId":{"type":"string","description":"Assistant that answers in the chat. Prefer PATCH /chats/{id}/assign-assistant, which validates it.","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"isUnderAiControl":{"type":"boolean","description":"Whether the assistant answers in this chat; `false` hands it to a human. Prefer PATCH /chats/{id}/ai-control.","example":true},"displayName":{"type":"string","description":"Not stored — the inbox shows `toName`. Kept for backward compatibility.","example":"John Doe"}}},"ChatDeletedModel":{"type":"object","properties":{"success":{"type":"boolean","description":"Always `true` when the chat was deleted","example":true}},"required":["success"]},"ToggleAiControlDto":{"type":"object","properties":{"isUnderAiControl":{"type":"boolean","description":"Target AI control state for the chat. `true` enables AI handling, `false` disables it.","example":false},"aiDisableReason":{"type":"string","description":"Reason for disabling AI. Applied only when `isUnderAiControl` is `false`. Defaults to `manual` when omitted while disabling.","enum":["manual","auto","insufficient_balance"],"example":"manual","nullable":true}},"required":["isUnderAiControl"]},"ChatAiControlResultModel":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable result","example":"ai disabled"},"aiControlEnabled":{"type":"boolean","description":"Whether the assistant now answers in the chat","example":false}},"required":["message","aiControlEnabled"]},"AssignAssistantDto":{"type":"object","properties":{"assistantId":{"type":"string","description":"ID of the assistant to assign to the chat","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"}},"required":["assistantId"]},"ChatAssistantResultModel":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable result","example":"Assistant assigned to chat"},"chat":{"description":"The updated chat","allOf":[{"$ref":"#/components/schemas/ChatModel"}]}},"required":["message","chat"]},"SetChatFolderDto":{"type":"object","properties":{"folderId":{"type":"object","description":"Folder to file the chat into; `null` or omitted takes it out of its folder","format":"uuid","nullable":true}}},"MessageReplyTargetModel":{"type":"object","properties":{"id":{"type":"string","description":"ID of the quoted message","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"role":{"type":"string","description":"Role of the quoted message","enum":["System","Assistant","User","Developer"],"example":"User"},"type":{"type":"string","description":"Type of the quoted message","enum":["Text","Audio","File","Document","Photo","Video","Specific","FunctionCallOutput","FunctionCall"],"example":"Text"},"text":{"type":"string","description":"Text of the quoted message","example":"Do you ship to Lviv?"},"createdAt":{"type":"string","description":"When the quoted message was sent","format":"date-time","example":"2026-10-07T09:15:00.000Z"}},"required":["id","role","type","createdAt"]},"MessageMediaModel":{"type":"object","properties":{"id":{"type":"string","description":"Attachment link ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"mediaId":{"type":"string","description":"Media ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"media":{"description":"The attached file","allOf":[{"$ref":"#/components/schemas/MediaModel"}]}},"required":["id","mediaId","media"]},"MessageModel":{"type":"object","properties":{"id":{"type":"string","description":"Message ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"chatId":{"type":"string","description":"Chat the message belongs to","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"role":{"type":"string","description":"Who wrote it: `User` — the customer (or a team member writing from the platform), `Assistant` — the AI, `System` — service notes. Tool frames use `Assistant` with a `FunctionCall*` type.","enum":["System","Assistant","User","Developer"],"example":"User"},"source":{"type":"string","description":"Where the message came from: a channel (`TELEGRAM`, `INSTAGRAM`, `WIDGET`…) for incoming messages, `AI` for assistant replies, `PLATFORM` for messages sent through the web app or this API.","enum":["TELEGRAM","TELEGRAM_BOT","INSTAGRAM","WHATSAPP","MESSENGER","ECHAT","CUSTOM_MESSENGER","WIDGET","PROM","ROZETKA","OLX","DIRECTHUB","SENDPULSE","SITNIKS","GMAIL","RESEND","PLATFORM","MOBILE","API","AI","PLAYGROUND"],"example":"TELEGRAM"},"type":{"type":"string","description":"Content type","enum":["Text","Audio","File","Document","Photo","Video","Specific","FunctionCallOutput","FunctionCall"],"example":"Text"},"specificType":{"type":"string","description":"Channel-specific subtype for `Specific` messages (stickers, contacts, locations…)","enum":["telegram_location","telegram_live_location","telegram_contact","telegram_poll","telegram_dice","telegram_venue","telegram_game","telegram_invoice","telegram_story","telegram_giveaway","telegram_giveaway_results","telegram_paid_media","telegram_unsupported","instagram_story","instagram_reel","instagram_post","whatsapp_location","whatsapp_contact","whatsapp_poll","whatsapp_event"]},"text":{"type":"string","description":"Message text","example":"Hi! Is the red dress available in size M?"},"transcription":{"type":"string","description":"Speech-to-text of a voice message","example":"Hello, I'd like to order…"},"audioUrl":{"type":"string","description":"Voice message URL","example":"https://cdn.happ.tools/audio/a1b2.ogg"},"fileUrl":{"type":"string","description":"File URL (legacy single-file field; see `messageMedia`)","example":"https://cdn.happ.tools/files/a1b2.pdf"},"fromId":{"type":"string","description":"Sender ID in the channel","example":"123456789"},"toId":{"type":"string","description":"Recipient ID in the channel","example":"6340798777"},"externalId":{"type":"string","description":"Message ID in the source channel","example":"4711"},"replyToMessageId":{"type":"object","description":"ID of the message this one replies to","format":"uuid","nullable":true,"example":null},"replyToMessage":{"description":"The quoted message, when this is a reply","nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/MessageReplyTargetModel"}]},"messageMedia":{"description":"Attachments (photos, files, voice, video)","type":"array","items":{"$ref":"#/components/schemas/MessageMediaModel"}},"functionCallId":{"type":"string","description":"Tool call ID (tool frames only)","example":"call_8f2a"},"functionCallName":{"type":"string","description":"Name of the tool the assistant called (tool frames only)","example":"check_order_status"},"functionCallInput":{"type":"string","description":"Arguments the assistant passed to the tool (JSON)","example":"{\"orderId\":\"1042\"}"},"functionCallOutput":{"type":"string","description":"What the tool returned (JSON)","example":"{\"status\":\"shipped\"}"},"sessionId":{"type":"string","description":"Conversation session the message belongs to","format":"uuid"},"authorUserId":{"type":"object","description":"Team member who sent the message from the platform","format":"uuid","nullable":true,"example":null},"llmModel":{"type":"string","description":"LLM model that generated an AI reply","example":"gpt-4.1-mini"},"llmProvider":{"type":"string","description":"LLM provider that generated an AI reply","example":"openai"},"inputTokens":{"type":"number","description":"Prompt tokens spent on an AI reply","example":1200},"outputTokens":{"type":"number","description":"Completion tokens spent on an AI reply","example":85},"totalTokens":{"type":"number","description":"Total tokens spent on an AI reply","example":1285},"meta":{"type":"object","description":"Extra data: forwarded-from info, voice duration, file name, ad referral, tool call details","additionalProperties":true},"createdAt":{"type":"string","description":"When the message was stored","format":"date-time","example":"2026-10-07T09:15:00.000Z"},"updatedAt":{"type":"string","description":"Last update time","format":"date-time","example":"2026-10-07T09:15:00.000Z"}},"required":["id","chatId","role","source","type","createdAt","updatedAt"]},"MessageListModel":{"type":"object","properties":{"data":{"description":"Messages on this page, newest first","type":"array","items":{"$ref":"#/components/schemas/MessageModel"}},"totalCount":{"type":"number","description":"Total number of messages in the chat","example":57},"nextCursor":{"type":"object","description":"Pass as `cursor` to load older messages; `null` when there are no more. Returned in cursor mode.","nullable":true,"example":"eyJjcmVhdGVkQXQiOiIyMDI2LTEwLTA3In0"},"page":{"type":"number","description":"Page number, returned in offset (`skip`) mode","example":1}},"required":["data","totalCount"]},"CreateMessageDto":{"type":"object","properties":{"source":{"type":"string","enum":["TELEGRAM","TELEGRAM_BOT","INSTAGRAM","WHATSAPP","MESSENGER","ECHAT","CUSTOM_MESSENGER","WIDGET","PROM","ROZETKA","OLX","DIRECTHUB","SENDPULSE","SITNIKS","GMAIL","RESEND","PLATFORM","MOBILE","API","AI","PLAYGROUND"],"description":"Required by validation but not used: messages sent through the API are always stored with source `PLATFORM`","example":"API"},"role":{"type":"string","enum":["System","Assistant","User","Developer"],"description":"Use `User` together with `fromId` = the chat's `fromId` — that is how a team member's reply is recorded. `Assistant` marks the message as written by the AI.","example":"User"},"type":{"type":"string","enum":["Text","Audio","File","Document","Photo","Video","Specific","FunctionCallOutput","FunctionCall"],"description":"`Text` for text; `Photo`, `Video`, `Audio`, `File`, `Document` when sending `mediaIds`","example":"Text"},"specificType":{"type":"string","enum":["telegram_location","telegram_live_location","telegram_contact","telegram_poll","telegram_dice","telegram_venue","telegram_game","telegram_invoice","telegram_story","telegram_giveaway","telegram_giveaway_results","telegram_paid_media","telegram_unsupported","instagram_story","instagram_reel","instagram_post","whatsapp_location","whatsapp_contact","whatsapp_poll","whatsapp_event"],"description":"Channel-specific subtype; leave empty for regular messages"},"externalId":{"type":"string","description":"Your own ID for the message, e.g. from an external system","example":"crm-msg-881"},"sessionId":{"type":"string","description":"Conversation session to attach the message to; leave empty","format":"uuid"},"fromId":{"type":"string","description":"Sender ID in the channel. Pass the chat's `fromId` so the message counts as sent by your side.","example":"6340798777"},"toId":{"type":"string","description":"Recipient ID in the channel (the chat's `toId`); optional","example":"123456789"},"chatId":{"type":"string","description":"Chat to send to. Without it the message is stored but not delivered.","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"sendAsAccountId":{"type":"string","description":"Group chats: send through this connected account (its messenger id) instead of the chat's own. Must be a member of the group."},"replyToMessageId":{"type":"string","description":"Quote an earlier message of the same chat (its ID). Supported on Telegram, Telegram bots, Instagram and WhatsApp Business; on Facebook Messenger only for text (no `mediaIds`). Other channels reject it with 400."},"text":{"type":"string","description":"Message text; the caption when sent with `mediaIds`","example":"Your order #1042 has shipped!"},"transcription":{"type":"string","description":"Text version of a voice message; not sent to the customer"},"audioUrl":{"type":"string","description":"Legacy: URL of a voice file. Prefer `mediaIds`."},"fileUrl":{"type":"string","description":"Legacy: URL of a file. Prefer `mediaIds`."},"functionCallId":{"type":"string","description":"Tool frames only — not used when messaging a customer"},"functionCallName":{"type":"string","description":"Tool frames only — not used when messaging a customer"},"functionCallInput":{"type":"string","description":"Tool frames only — not used when messaging a customer"},"functionCallOutput":{"type":"string","description":"Tool frames only — not used when messaging a customer"},"mediaIds":{"description":"IDs of files uploaded to your company's media storage to attach (up to 10)","example":["c3d4e5f6-a7b8-9012-cdef-345678901234"],"type":"array","items":{"type":"string"}}},"required":["source","role","type"]},"AddChatLabelDto":{"type":"object","properties":{"label":{"type":"string","description":"Label text (1..64 chars)"}},"required":["label"]},"SetChatTagsDto":{"type":"object","properties":{"tagIds":{"description":"The chat's full tag set after the call","type":"array","items":{"type":"string","format":"uuid"}}},"required":["tagIds"]},"LinkChatTagDto":{"type":"object","properties":{"tagId":{"type":"string","description":"Tag to attach","format":"uuid"}},"required":["tagId"]},"AddChatCommentDto":{"type":"object","properties":{"text":{"type":"string","description":"Comment body (1..2000 chars)"}},"required":["text"]},"TestToolDto":{"type":"object","properties":{"params":{"type":"object","description":"Params forwarded to the webhook as the request body"}}},"AssistantToolTestResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Your endpoint answered with a 2xx status","example":true},"statusCode":{"type":"object","description":"HTTP status of your endpoint; null when it was unreachable","nullable":true,"example":200},"elapsedMs":{"type":"number","description":"Round-trip time in milliseconds","example":184},"response":{"type":"object","description":"Body returned by your endpoint (or the error body)","additionalProperties":true,"example":{"status":"shipped","eta":"2026-10-09"}}},"required":["success","elapsedMs","response"]},"KnowledgeQueryDto":{"type":"object","properties":{"assistantId":{"type":"string","format":"uuid","description":"Assistant whose knowledge base to search"},"query":{"type":"string","maxLength":2000,"description":"Search phrase, as a customer would ask it","example":"how long does delivery take"},"topK":{"type":"number","minimum":1,"maximum":20,"description":"Maximum number of chunks to return","default":5,"example":5}},"required":["assistantId","query"]},"KnowledgeSearchHitResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Knowledge item the chunk came from","format":"uuid"},"name":{"type":"string","description":"Knowledge item name","example":"Delivery and returns"},"content":{"type":"string","description":"Matched chunk text","example":"Returns are accepted within 14 days..."},"score":{"type":"number","description":"Similarity score, higher is closer","example":0.83},"path":{"type":"string","description":"Page path for URL items; empty otherwise","example":""},"chunkIndex":{"type":"number","description":"Chunk position within the item","example":2},"totalChunks":{"type":"number","description":"Number of chunks in the item","example":9}},"required":["id","name","content","score","path"]},"KnowledgeSearchResponseDto":{"type":"object","properties":{"data":{"description":"Matching chunks, best first","type":"array","items":{"$ref":"#/components/schemas/KnowledgeSearchHitResponseDto"}}},"required":["data"]},"ChatFolderModel":{"type":"object","properties":{"id":{"type":"string","description":"Folder ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"companyId":{"type":"string","description":"Company the folder belongs to","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"name":{"type":"string","description":"Folder name","example":"Wholesale"},"color":{"type":"object","description":"Hex color","nullable":true,"example":"#6366f1"},"createdAt":{"type":"string","description":"Created at (ISO 8601)","example":"2026-10-07T09:15:00.000Z"},"updatedAt":{"type":"string","description":"Updated at (ISO 8601)","example":"2026-10-07T09:15:00.000Z"}},"required":["id","companyId","name","createdAt","updatedAt"]},"CreateChatFolderDto":{"type":"object","properties":{"companyId":{"type":"string","description":"Company the folder belongs to","format":"uuid"},"name":{"type":"string","description":"Folder name (1..80 chars)","example":"Wholesale"},"color":{"type":"string","description":"Hex color shown next to the folder","example":"#6366f1"}},"required":["companyId","name"]},"UpdateChatFolderDto":{"type":"object","properties":{"name":{"type":"string","description":"Folder name (1..80 chars)","example":"Wholesale"},"color":{"type":"string","description":"Hex color shown next to the folder","example":"#6366f1"}}},"ChatTagModel":{"type":"object","properties":{"id":{"type":"string","description":"Tag ID","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"companyId":{"type":"string","description":"Company the tag belongs to","format":"uuid","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"name":{"type":"string","description":"Tag name, unique per company regardless of case","example":"vip"},"color":{"type":"string","description":"Hex color of the chip","example":"#6366f1"},"createdAt":{"type":"string","description":"Created at (ISO 8601)","example":"2026-10-07T09:15:00.000Z"},"updatedAt":{"type":"string","description":"Updated at (ISO 8601)","example":"2026-10-07T09:15:00.000Z"}},"required":["id","companyId","name","color","createdAt","updatedAt"]},"CreateChatTagDto":{"type":"object","properties":{"companyId":{"type":"string","description":"Company the tag belongs to","format":"uuid"},"name":{"type":"string","description":"Tag name (1..64 chars), unique per company regardless of case","example":"vip"},"color":{"type":"string","description":"Hex color of the tag chip","example":"#6366f1"}},"required":["companyId","name"]},"UpdateChatTagDto":{"type":"object","properties":{"name":{"type":"string","description":"Tag name (1..64 chars), unique per company regardless of case","example":"vip"},"color":{"type":"string","description":"Hex color of the tag chip","example":"#6366f1"}}},"CurrentUserResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"User id"},"email":{"type":"object","nullable":true,"description":"Email","example":"john.doe@example.com"},"tel":{"type":"object","nullable":true,"description":"Phone number","example":"+380501234567"},"firstName":{"type":"object","nullable":true,"description":"First name","example":"John"},"lastName":{"type":"object","nullable":true,"description":"Last name","example":"Doe"},"avatar":{"type":"object","nullable":true,"description":"Avatar image URL"},"verificationStatus":{"type":"string","enum":["Verified","NotVerified"],"description":"Whether the account email/phone is verified"},"createdAt":{"type":"string","format":"date-time","description":"Registration time"}},"required":["id","verificationStatus","createdAt"]},"AuthTokensResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","description":"JWT for `Authorization: Bearer <jwt>`. Valid for 15 minutes","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."},"refreshToken":{"type":"string","description":"Refresh token for `POST /api/auth/refresh`. Valid for 7 days","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."},"expiresIn":{"type":"string","description":"Lifetime of `accessToken`","example":"15m"},"invitedCompanyId":{"type":"string","format":"uuid","description":"Present when `inviteToken` was accepted: the company the user just joined"}},"required":["accessToken","refreshToken","expiresIn"]},"SignInRequestDto":{"type":"object","properties":{"email":{"type":"string","description":"Account email","example":"john.doe@example.com"},"password":{"type":"string","description":"Account password","example":"S3cure-passw0rd"},"inviteToken":{"type":"string","description":"Company invitation token; when provided, the user joins the inviting company on sign-in","example":"8f14e45fceea167a5a36dedd4bea2543"}},"required":["email","password"]},"ConnectChatWebhookDto":{"type":"object","properties":{"webhookUrl":{"type":"string","description":"Public URL on your server that receives message events (use HTTPS)","example":"https://example.com/happ/webhook"},"webhookSecret":{"type":"string","description":"Shared secret used to sign every delivery (`X-Signature: sha256=<hmac>`). Strongly recommended — without it requests are unsigned. Omit on a repeated connect to keep the current one.","example":"whsec_3f9a1c7e2b"},"chatOwnerIds":{"description":"Limit deliveries to chats of these connected accounts (the chat's `chatOwnerId`). Omit to receive every chat of the company.","example":["123456789"],"type":"array","items":{"type":"string"}}},"required":["webhookUrl"]},"CompanyIntegrationListResponseDto":{"type":"object","properties":{"data":{"description":"Connected integrations","type":"array","items":{"$ref":"#/components/schemas/CompanyIntegrationResponseDto"}},"totalCount":{"type":"number","description":"Total number of matching integrations","example":1}},"required":["data","totalCount"]},"DeleteIntegrationResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"`false` when no integration with this ID exists","example":true}},"required":["success"]}}}}