{"openapi":"3.1.0","info":{"title":"DOS AI Public API","version":"1.0.0","summary":"Manage DOS AI projects, leads, conversations and analytics programmatically.","description":"REST API for the DOS AI platform (dosai.pro): AI assistants for WhatsApp and Telegram with a built-in CRM.\n\nAuthentication uses a long-lived API key issued in the cabinet (Developers section):\n`Authorization: Bearer dos_sk_live_…`. The key authenticates AS the user who created it, so it\nsees exactly the projects that user sees and can do exactly what their role on each project allows.\n\nKeys carry two independent restrictions:\n- scope `read` (GET only) or `read`+`write` (mutations allowed);\n- account-wide (all of the owner's projects) or locked to a single project.\n\nA project-scoped key may only use `/api/projects/{id}/…` for its own project; account-level paths\nsuch as `GET /api/projects` return 403 API_KEY_PROJECT_SCOPE for it.\n\nAI agents: an MCP server exposing this same catalogue as typed tools is available at `/api/mcp`\nwith the same bearer token. See /llms.txt for connection instructions.","contact":{"name":"DOS AI","url":"https://dosai.pro/docs/guide/08-developers/80-public-api"},"termsOfService":"https://dosai.pro/offer","license":{"name":"Proprietary","url":"https://dosai.pro/offer"}},"servers":[{"url":"https://dosai.pro","description":"Production"}],"security":[{"dosApiKey":[]}],"tags":[{"name":"Projects","description":"Projects: create, read, update, delete."},{"name":"Leads","description":"Leads captured by the assistant."},{"name":"Conversations","description":"Dialogs and messages; operator replies."},{"name":"Functions","description":"Tools the assistant can call."},{"name":"Integrations","description":"Outbound webhooks."},{"name":"Analytics","description":"Aggregated project metrics."},{"name":"Billing","description":"Read-only balance and usage history."}],"paths":{"/api/projects":{"get":{"tags":["Projects"],"operationId":"listProjects","summary":"List the caller's projects","description":"Returns every project the authenticated user owns or is a member of. Not available to project-scoped keys.","x-rate-limit":"30/min per user","responses":{"200":{"description":"Projects array (returned bare, not wrapped). The list omits `system_prompt`: it is the longest column a project has and nothing reading a list needs it. Fetch one project to get its prompt.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectListItem"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Projects"],"operationId":"createProject","summary":"Create a project","description":"Creates a project owned by the caller. `system_prompt` is deliberately NOT accepted here: create the project, then set the prompt with PATCH.","x-rate-limit":"10/min per user","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreate"},"example":{"name":"Barbershop bot","company_name":"Sharp Cuts","industry":"beauty"}}}},"responses":{"201":{"description":"Created project, wrapped in `data`. `warning` explains a non-blocking caveat (no trial balance on an extra project); `trial_grant_pending` means the starting balance is still being credited, so a balance read right away may show zero.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Project"},"warning":{"type":"string"},"trial_grant_pending":{"type":"boolean"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Projects"],"operationId":"getProject","summary":"Get a project","description":"Field set depends on the caller's role: owner/admin receive the full record including `system_prompt`; viewer/editor receive a restricted set without prompt, model and wiring URLs.","x-rate-limit":"30/min per user","responses":{"200":{"description":"Project (returned bare, not wrapped).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"tags":["Projects"],"operationId":"updateProject","summary":"Update project settings","description":"Partial update. Requires role admin or owner on the project, and a key with `write` scope. Only the listed fields are accepted; unknown fields are rejected.","x-rate-limit":"20/min per user","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUpdate"},"example":{"system_prompt":"Ты администратор барбершопа. Отвечай кратко.","status":"active"}}}},"responses":{"200":{"description":"Updated project. `data` is null when the update touched wiring the caller may not read back; `refetch_required` then asks the client to re-read the project rather than trust its local copy.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Project"},"success":{"type":"boolean"},"refetch_required":{"type":"boolean"}},"required":["success"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Projects"],"operationId":"deleteProject","summary":"Delete a project","description":"Owner only, irreversible: deletes the project together with its conversations, leads and settings.","x-rate-limit":"5/min per user","responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/leads":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Leads"],"operationId":"listLeads","summary":"List leads","description":"Newest first. Pages 1-20 use offset pagination; beyond that pass the `before` cursor returned as `pagination.next_before` (keyset mode) to keep results stable.","x-rate-limit":"30/min per user","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"name":"before","in":"query","description":"Keyset cursor: `created_at` of the last row from the previous page.","schema":{"type":"string","format":"date-time"}},{"name":"status","in":"query","description":"Filter by lead status. An unknown value returns 400 rather than an empty page.","schema":{"type":"string","enum":["none","potential","qualified","hot","converted","reserve","lost"]}}],"responses":{"200":{"description":"Leads page.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Leads"],"operationId":"deleteLeads","summary":"Delete leads in bulk","x-rate-limit":"10/min per user","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["ids"]}}}},"responses":{"200":{"description":"Deleted. `deleted` is how many rows actually went, which can be lower than the ids sent if some were already gone.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"deleted":{"type":"integer"}},"required":["success"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/leads/{leadId}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}},{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Leads"],"operationId":"getLead","summary":"Get a lead","x-rate-limit":"30/min per user","responses":{"200":{"description":"Lead, wrapped in `data`.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Lead"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"tags":["Leads"],"operationId":"updateLead","summary":"Update a lead","description":"`lead_status` accepts a built-in status or any custom status configured for this project; an invalid value returns 400 listing the valid ones.","x-rate-limit":"30/min per user","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadUpdate"},"example":{"lead_status":"qualified","client_name":"Айгерим"}}}},"responses":{"200":{"description":"Updated lead, wrapped in `data`.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Lead"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/leads/export":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Leads"],"operationId":"exportLeads","summary":"Export leads as CSV","x-rate-limit":"10/min per user","responses":{"200":{"description":"CSV file.","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/conversations":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Conversations"],"operationId":"listConversations","summary":"List conversations","x-rate-limit":"60/min per user","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"status","in":"query","description":"Filter by lifecycle status.","schema":{"type":"string","enum":["open","resolved","closed"]}},{"name":"include_archived","in":"query","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Conversations page.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/conversations/{convId}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}},{"name":"convId","in":"path","required":true,"description":"Conversation UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Conversations"],"operationId":"getConversation","summary":"Get a conversation","x-rate-limit":"30/min per user","responses":{"200":{"description":"Conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Conversation"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"tags":["Conversations"],"operationId":"updateConversation","summary":"Update a conversation","description":"Rename the client, fix their phone, retag, pin, archive or close the dialog. Send only the fields that change.","x-rate-limit":"30/min per user","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationUpdate"},"example":{"client_name":"Айгерим","tags":["vip"]}}}},"responses":{"200":{"description":"Updated conversation, wrapped in `data`.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Conversation"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/conversations/{convId}/messages":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}},{"name":"convId","in":"path","required":true,"description":"Conversation UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Conversations"],"operationId":"listMessages","summary":"List messages of a conversation","description":"Ordered by real send time, so re-synced history interleaves correctly with live turns.","x-rate-limit":"60/min per user","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":100}}],"responses":{"200":{"description":"Messages page.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/conversations/{convId}/send":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}},{"name":"convId","in":"path","required":true,"description":"Conversation UUID.","schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Conversations"],"operationId":"sendOperatorMessage","summary":"Send a message to the client as operator","description":"Delivers text to the end client over the project's channel and records it in the transcript.\n\nSend an `Idempotency-Key` header: a repeat of the same key within 5 minutes is rejected with 409\ninstead of delivering the message twice. Without it, a network retry means the client sees two messages.","x-rate-limit":"30/min per user","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique per logical send. Up to 100 chars of [A-Za-z0-9._-].","schema":{"type":"string","maxLength":100,"pattern":"^[A-Za-z0-9._-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"description":"Message body."},"reply_to_message_id":{"type":"string","format":"uuid","description":"Quote an earlier message of this conversation."}},"required":["text"]},"example":{"text":"Здравствуйте! Записала вас на 15:00."}}}},"responses":{"200":{"description":"Accepted. CHECK `ok`: true means the channel took the message; false means it did not go out (see `error`), the attempt is stored as a failed message and can be retried. HTTP 207 means delivered but not yet persisted in the transcript.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"message_id":{"type":"string","format":"uuid"},"send_status":{"type":"string","examples":["failed"]},"error":{"type":"string","description":"Present when ok is false: why the channel refused."},"channel_stale":{"type":"boolean"},"warning":{"type":"string"}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"The channel refused it and even the failed-attempt record could not be stored; nothing was delivered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/projects/{id}/functions":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Functions"],"operationId":"listFunctions","summary":"List the assistant's functions","description":"Secret values inside `builtin_config` (tokens, API keys, passwords) are masked in the response and never leave the server.","x-rate-limit":"60/min per user","responses":{"200":{"description":"Functions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunctionList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/integrations":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Integrations"],"operationId":"listIntegrations","summary":"List outbound webhooks","x-rate-limit":"30/min per user","responses":{"200":{"description":"Integrations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Integrations"],"operationId":"createIntegration","summary":"Create an outbound webhook","description":"The URL is validated against SSRF: private, loopback and link-local addresses are refused.","x-rate-limit":"20/min per user","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationCreate"},"example":{"name":"CRM sync","type":"webhook","url":"https://example.com/hooks/dosai","events":["lead.created"]}}}},"responses":{"201":{"description":"Created integration, wrapped in `data`.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Integration"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/integrations/{intId}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}},{"name":"intId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"put":{"tags":["Integrations"],"operationId":"updateIntegration","summary":"Update a webhook","x-rate-limit":"20/min per user","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationCreate"}}}},"responses":{"200":{"description":"Updated integration, wrapped in `data`.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Integration"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Integrations"],"operationId":"deleteIntegration","summary":"Delete a webhook","x-rate-limit":"20/min per user","responses":{"200":{"description":"Deleted; `data.id` echoes the removed integration.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}}}},"required":["success"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/analytics":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Analytics"],"operationId":"getAnalytics","summary":"Project metrics for a period","description":"Dialogs, leads, conversion, response time and daily series. A dialog counts only when the assistant actually replied.","x-rate-limit":"20/min per user","parameters":[{"name":"from","in":"query","description":"YYYY-MM-DD (Asia/Almaty).","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"YYYY-MM-DD (Asia/Almaty).","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Metrics.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Analytics"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/reports":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Analytics"],"operationId":"getMonthlyReportPdf","summary":"Monthly report as PDF","x-rate-limit":"5/min per user","parameters":[{"name":"period","in":"query","description":"YYYY-MM. Defaults to the current month; out-of-range values fall back to it.","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"}}],"responses":{"200":{"description":"PDF file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/tokens":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Billing"],"operationId":"getBalance","summary":"Project balance","x-rate-limit":"30/min per user","responses":{"200":{"description":"Balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenBalance"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/projects/{id}/tokens/history":{"parameters":[{"name":"id","in":"path","required":true,"description":"Project UUID.","schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Billing"],"operationId":"getBalanceHistory","summary":"Balance transactions","x-rate-limit":"30/min per user","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Transactions page.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TokenTransaction"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"dosApiKey":{"type":"http","scheme":"bearer","description":"API key issued in the cabinet: `Authorization: Bearer dos_sk_live_…`. Only the SHA-256 hash is stored, so a lost key cannot be recovered, only revoked and reissued."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message."},"code":{"type":"string","description":"Stable machine-readable code.","examples":["UNAUTHORIZED","INSUFFICIENT_SCOPE","API_KEY_PROJECT_SCOPE","NOT_FOUND","RATE_LIMITED"]}},"required":["error"]},"Pagination":{"type":"object","description":"Offset mode returns page/limit/total/pages; keyset mode (deep lead pages) returns mode/limit/has_more/next_before. The functions list uses offset/limit/total/has_more instead of page numbers.","properties":{"mode":{"type":"string","enum":["offset","keyset"]},"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"pages":{"type":"integer","description":"Total number of pages in offset mode."},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"next_before":{"type":["string","null"],"format":"date-time"}}},"Project":{"type":"object","description":"Fields vary by role: viewer/editor do not receive system_prompt, openai_model, blacklist or notify/crm wiring.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"company_name":{"type":"string"},"industry":{"type":"string"},"description":{"type":"string"},"system_prompt":{"type":"string","description":"Owner/admin only."},"status":{"type":"string","enum":["draft","active","paused","archived"]},"channel":{"type":"string","examples":["whatsapp","telegram"]},"color":{"type":"string"},"openai_model":{"type":"string","description":"Owner/admin only."},"token_balance":{"type":"integer","description":"Balance in US cents."},"tokens_used":{"type":"integer"},"total_conversations":{"type":"integer"},"total_leads":{"type":"integer"},"subscription_active":{"type":"boolean"},"subscription_expires_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","name","status"]},"ProjectCreate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"company_name":{"type":"string","minLength":1,"maxLength":200},"industry":{"type":"string","maxLength":100},"description":{"type":"string","maxLength":40000},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{3,8}$"}},"required":["name","company_name"]},"ProjectUpdate":{"type":"object","description":"All fields optional; send only what changes.","properties":{"name":{"type":"string","maxLength":200},"company_name":{"type":"string","maxLength":200},"industry":{"type":"string","maxLength":100},"description":{"type":"string","maxLength":40000},"system_prompt":{"type":"string","maxLength":100000},"status":{"type":"string","enum":["draft","active","paused","archived"]},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{3,8}$"},"notify_email":{"type":["string","null"],"format":"email"}}},"Lead":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"project_id":{"type":"string","format":"uuid"},"conversation_id":{"type":["string","null"],"format":"uuid"},"client_name":{"type":["string","null"]},"phone":{"type":["string","null"]},"email":{"type":["string","null"]},"lead_status":{"type":"string"},"full_data":{"type":["object","null"],"additionalProperties":true},"summary":{"type":["string","null"]},"operator_notes":{"type":["string","null"]},"source":{"type":["string","null"]},"source_detail":{"type":["string","null"]},"tags":{"type":["array","null"],"items":{"type":"string"}},"deal_value":{"type":["number","null"]},"deal_currency":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","project_id","lead_status"]},"LeadUpdate":{"type":"object","properties":{"lead_status":{"type":"string","maxLength":64},"client_name":{"type":"string","maxLength":200},"phone":{"type":"string"},"email":{"type":"string","format":"email"}}},"Conversation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"project_id":{"type":"string","format":"uuid"},"chat_id":{"type":"string"},"chat_name":{"type":["string","null"]},"client_name":{"type":["string","null"]},"phone":{"type":["string","null"]},"stage":{"type":["string","null"]},"lead_status":{"type":["string","null"]},"status":{"type":"string","enum":["open","resolved","closed"]},"ai_on":{"type":"boolean"},"ai_engaged":{"type":"boolean","description":"The assistant has replied at least once; this is what makes the dialog count in analytics."},"ai_paused_at":{"type":["string","null"],"format":"date-time"},"messages_count":{"type":"integer"},"last_message_at":{"type":["string","null"],"format":"date-time"},"summary":{"type":["string","null"]},"tags":{"type":["array","null"],"items":{"type":"string"}},"language":{"type":["string","null"]},"assigned_user_id":{"type":["string","null"],"format":"uuid"},"archived_at":{"type":["string","null"],"format":"date-time"},"pinned_at":{"type":["string","null"],"format":"date-time"},"snoozed_until":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id"]},"ProjectListItem":{"type":"object","description":"A project as returned by the list endpoint, without `system_prompt`.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"company_name":{"type":"string"},"industry":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["draft","active","paused","archived"]},"channel":{"type":"string","examples":["whatsapp","telegram"]},"color":{"type":"string"},"openai_model":{"type":"string","description":"Owner/admin only."},"token_balance":{"type":"integer","description":"Balance in US cents."},"tokens_used":{"type":"integer"},"total_conversations":{"type":"integer"},"total_leads":{"type":"integer"},"subscription_active":{"type":"boolean"},"subscription_expires_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"ConversationUpdate":{"type":"object","description":"All fields optional; send only what changes.","properties":{"client_name":{"type":"string"},"phone":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["open","resolved","closed"]},"archived_at":{"description":"true archives now, false or null un-archives, or pass an ISO timestamp.","anyOf":[{"type":"boolean"},{"type":"string","format":"date-time"},{"type":"null"}]},"pinned_at":{"description":"Same shape as archived_at, for pinning.","anyOf":[{"type":"boolean"},{"type":"string","format":"date-time"},{"type":"null"}]}}},"Message":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["user","assistant","system"]},"content":{"type":["string","null"]},"message_type":{"type":"string","description":"`operator` marks a human reply; other values describe media.","examples":["text","voice","image","document","operator","sticker"]},"media_url":{"type":["string","null"]},"reply_to_message_id":{"type":["string","null"],"format":"uuid"},"created_at":{"type":"string","format":"date-time"}},"required":["id","role"]},"FunctionList":{"type":"object","properties":{"pagination":{"$ref":"#/components/schemas/Pagination"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"action_type":{"type":"string"},"is_active":{"type":"boolean"},"category":{"type":["string","null"]},"secret_keys":{"type":"array","items":{"type":"string"},"description":"Names of config keys whose values were masked."}}}}}},"Integration":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"project_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"},"is_active":{"type":"boolean"},"events":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"IntegrationList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Integration"}}}},"IntegrationCreate":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"},"url":{"type":"string","maxLength":2048},"events":{"type":"array","items":{"type":"string"}},"headers":{"type":"object","additionalProperties":{"type":"string"}},"is_active":{"type":"boolean","default":true}},"required":["name","url"]},"Analytics":{"type":"object","description":"Metrics for the requested period. Money-related members (`spending`) are present only for the project owner. `*Incomplete` flags mean a section could not be computed in full and must not be read as zeros.","properties":{"role":{"type":"string","description":"Caller's role on the project."},"isOwner":{"type":"boolean"},"stats":{"type":"object","properties":{"periodConversations":{"type":"integer","description":"Dialogs in the period; a dialog counts only once the assistant replied."},"periodLeads":{"type":"integer"},"conversionRate":{"type":["number","null"],"description":"Percent, leads over dialogs."},"todayConversations":{"type":"integer"},"todayLeads":{"type":"integer"}}},"avgResponseTimeSec":{"type":["number","null"]},"timeToConvert":{"type":["number","null"],"description":"Seconds from first message to lead."},"dailyData":{"type":"array","description":"Daily series for the period.","items":{"type":"object","properties":{"date":{"type":"string"},"conversations":{"type":"integer"},"leads":{"type":"integer"}}}},"leadsByStatus":{"type":"array","items":{"type":"object","additionalProperties":true}},"leadsBySource":{"type":"array","items":{"type":"object","additionalProperties":true}},"leadsByTag":{"type":"array","items":{"type":"object","additionalProperties":true}},"statusFunnel":{"type":"array","items":{"type":"object","additionalProperties":true}},"dialogsByChannel":{"type":"object","additionalProperties":true},"sourceBreakdowns":{"type":"object","additionalProperties":true},"adCreatives":{"type":"array","items":{"type":"object","additionalProperties":true}},"campaigns":{"type":"array","items":{"type":"object","additionalProperties":true}},"quality":{"type":"object","additionalProperties":true},"hourlyHeatmap":{"type":"array","items":{"type":"object","additionalProperties":true}},"topUnanswered":{"type":"array","items":{"type":"object","additionalProperties":true}},"spending":{"type":"object","description":"Owner only.","additionalProperties":true},"spendingUnavailable":{"type":"boolean","description":"Owner only: spend could not be read, which is not the same as zero spend."},"dataIncomplete":{"type":"boolean"},"heatmapDataIncomplete":{"type":"boolean"},"funnelDataIncomplete":{"type":"boolean"}}},"TokenBalance":{"type":"object","properties":{"token_balance":{"type":"integer","description":"Balance in US cents."},"tokens_used":{"type":"integer"},"balance_usd":{"type":"number"},"balance_formatted":{"type":"string","description":"Display string with the live KZT rate applied."}}},"TokenTransaction":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"project_id":{"type":"string","format":"uuid"},"amount":{"type":"integer","description":"US cents; negative for spend."},"balance_after":{"type":"integer"},"operation":{"type":"string","examples":["usage","topup","refund"]},"description":{"type":["string","null"]},"source":{"type":["string","null"]},"channel":{"type":["string","null"]},"model_id":{"type":["string","null"]},"raw_tokens":{"type":["integer","null"],"description":"Model tokens behind this charge, when the row came from an AI call."},"created_at":{"type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing, invalid, expired or revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized","code":"UNAUTHORIZED"}}}},"Forbidden":{"description":"Not allowed. Distinguish by `code`: `INSUFFICIENT_SCOPE` (read-only key attempted a mutation), `API_KEY_PROJECT_SCOPE` (project-scoped key reached outside its project), `FORBIDDEN` (the user's role on this project is too low), `ACCOUNT_BANNED`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"This API key is read-only","code":"INSUFFICIENT_SCOPE"}}}},"NotFound":{"description":"No such resource, or it belongs to another account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflicting state, e.g. a repeated Idempotency-Key or a delete already in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests. 120/min per key overall, plus a stricter per-endpoint budget. Retry after the number of seconds in Retry-After.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix seconds."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Too many requests","code":"RATE_LIMITED"}}}}}}}