{
  "openapi": "3.1.0",
  "info": {
    "title": "OneVOIPlanet Client API",
    "version": "3.1.0",
    "contact": {
      "name": "OneVOIPlanet Support",
      "email": "support@onevoiplanet.com",
      "url": "https://onevoiplanet.ua"
    },
    "termsOfService": "https://onevoiplanet.ua/terms-of-use",
    "description": "# Getting started\n\nOneVOIPlanet is a cloud telephony platform: virtual numbers in 100+ countries, SIP lines for employees, IVR, call groups, call records and statistics, SMS, eSIM and callback widgets. Everything you can do in the web cabinet (`https://cabinet.onevoiplanet.ua`) and in the OnePhone mobile app is available through this REST API.\n\n**Base URL:** `https://api.onevoiplanet.com/api`\n\nAll paths in this documentation are relative to the base URL. Only HTTPS is accepted.\n\n## 1. Get a token\n\nSign in with the email (or phone) and password of a cabinet user:\n\n```bash\ncurl -X POST \"https://api.onevoiplanet.com/api/auth/local\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"identifier\": \"owner@example.com\", \"password\": \"********\"}'\n```\n\nResponse:\n\n```json\n{\n  \"jwt\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"user\": { \"id\": 42, \"username\": \"owner@example.com\", \"email\": \"owner@example.com\" }\n}\n```\n\n## 2. Call the API\n\nSend the token in the `Authorization` header of every request:\n\n```bash\ncurl \"https://api.onevoiplanet.com/api/companies/my\" \\\n  -H \"Authorization: Bearer $TOKEN\"\n```\n\n## 3. Token lifetime and renewal\n\n* The JWT is valid for **30 days** after issue. There is no refresh endpoint: when you receive `401`, sign in again with `POST /auth/local` and replace the stored token.\n* `POST /auth/logout` only returns `204`; it does not invalidate the token. Keep the token secret and never put it in URLs.\n* Changing the password does not invalidate previously issued tokens.\n\n## 4. Tokens for integrations\n\nFor a server-to-server integration (your CRM, your own system) do not use a personal login. Request a dedicated API token:\n\n* `POST /crm-token/create-token` issues a company API token with read-only scope (`/companies/my`, `/companies/getEmployees`, `POST /statistics/myStat`) and a lifetime of 7 days. **At the moment this method is not enabled for client roles**; until it is, ask support to issue an API token for your company. The token is sent the same way: `Authorization: Bearer <api-token>`.\n* An API token acts on behalf of the company, not of a user, so endpoints that depend on the current user (`/users/me`, `/sip/my`) are not available to it.\n\n## 5. Sandbox\n\nThere is no public sandbox. Use a separate test company in production: register it with `POST /auth/local/register`, top it up with a small amount and test on it. Methods that charge money are marked in the **Side effects** block of every operation.\n\n## 6. Postman\n\nA Postman collection (EN) with the same grouping is available on the documentation page. Set the collection variables `baseUrl` (`https://api.onevoiplanet.com/api`) and `token`.\n\n# Authentication\n\n| Scheme | Where | Who |\n|---|---|---|\n| `Authorization: Bearer <JWT>` | header | cabinet users (owner, supervisor, finance, employee) |\n| `Authorization: Bearer <API token>` | header | external systems (company-wide token) |\n| none | — | public methods: registration, login, password recovery, public catalogues, callback widget requests |\n\n## Roles\n\nEvery method lists the roles that may call it in the **Required role** block.\n\n| Role | Who gets it | Typical access |\n|---|---|---|\n| Company owner (registered, not yet approved) | the user who registered a company | profile, company, orders, first top-up |\n| Company owner (approved) | owner after the company is approved | everything in the company |\n| Supervisor | manager appointed by the owner | telephony settings, employees, statistics; no payments and no deletion of the company |\n| Finance | accountant | balance, payments, acts, orders; read-only telephony |\n| Employee | regular user with a SIP line | own profile, own SIP, own calls, contacts, notifications |\n\nThe server always scopes data to the company of the authenticated user. Objects of another company return `403` or `404`.\n\n## Common errors of authentication\n\n* `401 UnauthorizedError` — the token is missing, malformed or expired.\n* `403 ForbiddenError` — the token is valid but the role has no permission for this method.\n* `429 RateLimitError` — too many login attempts; see **Rate limits**.\n\n# Conventions\n\n## Format\n\n* Requests and responses are JSON (`Content-Type: application/json; charset=utf-8`). Exceptions are explicitly marked: file uploads (`multipart/form-data`), PDF downloads (`application/pdf`).\n* Two response shapes exist:\n  * **Collection-type methods** (standard list / get / create / update / delete) return the Strapi envelope: `{ \"data\": { \"id\": 1, \"attributes\": { ... } }, \"meta\": { ... } }`. Lists return `data` as an array and `meta.pagination`.\n  * **Action methods** (`/companies/my`, `/statistics/myStat`, `/orders/calculate-order-price` and others) return a flat object described in the method itself.\n* Unknown fields in requests are ignored; private and system fields are never returned.\n\n## Dates and times\n\nISO-8601 in UTC, for example `2026-10-02T09:15:30.000Z`. Date-only parameters use `YYYY-MM-DD`. Billing runs at 03:00 UTC.\n\n## Money and currencies\n\n* Amounts are decimal numbers (`229.00`), not integers in cents.\n* The currency is fixed per company at registration (`USD` or `UAH`) and is returned in `/companies/my`. Prices in catalogues are returned in the company currency where the method says so; public catalogues return USD.\n* Negative amounts in payments mean charges, positive mean top-ups.\n\n## Phone numbers\n\nE.164 digits without the plus sign: `380441234567`, `15551234567`. Fields named `number` may contain only the local part (for example `1234567`), the full number is always in `fullnumber`.\n\n## Call statistics periods\n\nMethods that read call details from the billing system (`GET /companies/outCalls`, `GET /companies/inCalls`, and any method with `start_date`/`end_date` that reports call costs) accept a period of **at most 7 days**. A longer period returns `406 Not acceptable` with the message `Not acceptable: Time period is bigger than 7 days`. Split longer ranges into weekly requests. The call journal `GET /statistics` is not subject to this limit, use `filters[createdAt]` and pagination there.\n\n## Pagination\n\nStandard list methods accept `pagination[page]` (from 1) and `pagination[pageSize]` (1–100, default 25) and return:\n\n```json\n\"meta\": { \"pagination\": { \"page\": 1, \"pageSize\": 25, \"pageCount\": 4, \"total\": 87 } }\n```\n\n## Filters, sorting, populate\n\nStandard list methods accept the Strapi query syntax:\n\n* `filters[field][$op]=value` — operators `$eq`, `$ne`, `$lt`, `$lte`, `$gt`, `$gte`, `$in`, `$notIn`, `$contains`, `$containsi`, `$null`, `$notNull`, `$between`. Relations: `filters[company][id][$eq]=42`.\n* `sort=createdAt:desc` or `sort[0]=status:asc&sort[1]=createdAt:desc`.\n* `populate=*` or `populate[0]=phones&populate[1]=domain` to include relations; `fields[0]=id&fields[1]=name` to limit attributes.\n\nEncode brackets in URLs (`%5B`, `%5D`) or let your HTTP client do it. Filters are always combined with the server-side filter by your company.\n\n# Errors\n\nAll errors share one envelope:\n\n```json\n{\n  \"data\": null,\n  \"error\": {\n    \"status\": 403,\n    \"name\": \"ForbiddenError\",\n    \"message\": \"Forbidden\",\n    \"details\": {}\n  }\n}\n```\n\n| HTTP | `error.name` | When | What to do |\n|---|---|---|---|\n| 400 | `ValidationError` | a required field is missing or has a wrong type; `details.errors[]` lists fields | fix the request |\n| 400 | `ApplicationError` | business rule violated (number already taken, order cannot be paid, integration already exists); `message` explains | read `message`, do not retry blindly |\n| 401 | `UnauthorizedError` | no token, bad token, expired token | sign in again |\n| 402 | `PaymentRequiredError` | not enough money on the balance for the operation | top up the balance and repeat |\n| 403 | `ForbiddenError` | role has no permission, or the object belongs to another company | check the role |\n| 404 | `NotFoundError` | object does not exist or is not visible to your company | check the id |\n| 406 | `ApplicationError` | call statistics requested for a period longer than 7 days | split the period into chunks of ≤ 7 days |\n| 405 | `MethodNotAllowedError` | wrong HTTP method | check the method |\n| 413 | `PayloadTooLargeError` | upload is too big | reduce the file |\n| 429 | `RateLimitError` | rate limit exceeded; header `Retry-After` in seconds | wait and retry |\n| 500 | `InternalServerError` | server error | retry later, contact support with time and path |\n| 501 | `NotImplementedError` | the method is disabled | contact support |\n\nExamples of `ValidationError` details:\n\n```json\n{\n  \"data\": null,\n  \"error\": {\n    \"status\": 400,\n    \"name\": \"ValidationError\",\n    \"message\": \"2 errors occurred\",\n    \"details\": {\n      \"errors\": [\n        { \"path\": [\"identifier\"], \"message\": \"identifier is a required field\", \"name\": \"ValidationError\" },\n        { \"path\": [\"password\"], \"message\": \"password is a required field\", \"name\": \"ValidationError\" }\n      ]\n    }\n  }\n}\n```\n\n# Rate limits\n\nLimits apply per client IP address. When exceeded, the server returns `429` with the header `Retry-After` (seconds). Other methods have no fixed limit; keep a sensible pace (a few requests per second) and use pagination.\n\n| Methods | Limit |\n|---|---|\n| `POST /auth/local`, `/auth/local/register`, `/auth/forgot-password`, `/auth/reset-password` | 10 requests / 60 s |\n| `POST /callback-widgets/*`, `POST /callback-widget-tickets`, `/callback-widget-tickets/status`, `/register-tickets/register` | 20 requests / 300 s |\n| `GET|POST /orders/process-payment/*`, `/orders/validate/*` (payment return pages) | 30 requests / 300 s |\n| `POST /orders/calculate-order-price` | 30 requests / 60 s |\n| `/didww-identities*`, `POST /didww-verifications` | 120 requests / 300 s |\n| `GET /didww-catalog/public/*` | 120 requests / 60 s |\n\n# Idempotency\n\nThe API does **not** support idempotency keys. Rules for methods that move money:\n\n* `POST /orders` creates a new order on every call. Before creating an order call `POST /orders/calculate-order-price` to see the exact amount. If the connection drops after sending `POST /orders`, do **not** repeat the request immediately: first check `GET /orders?sort=createdAt:desc` for the order you just created and its status.\n* Card payments (`refill` orders) are completed on the payment provider's page; a repeated `POST /orders` creates a second unpaid order, not a second charge.\n* `POST /subscriptions/{id}/restore` is protected against double charging: a second call within 2 minutes returns `processing` instead of charging again.\n* `POST /sms/outgoing/send` sends and charges on every call. Keep your own message ids to avoid duplicates.\n* `POST /e-sim/buyBundle/{id}` charges on every call.\n\n# Webhooks\n\n**Status: outgoing webhooks to an arbitrary client URL are not available yet.** The platform does not have a setting where a client registers a URL and receives events. This section describes honestly what exists today and how the gap is planned to be closed.\n\n## What exists today\n\n| Channel | What is delivered | How to enable |\n|---|---|---|\n| **CRM integrations** | Call events are pushed into your CRM through its own API: `Ringing` (incoming call started, codes 100/180/183), `inCall` (answered, 200 OK), `NormalHangup` (finished), `Hangup` (failed / not answered), `Refered` (transferred, 302). The payload contains the caller and callee numbers, direction, the employee SIP line, call id, setup/start/end times and the record link when available. | Connect a CRM in **Integrations** (`POST /companies/create-integration/{serviceName}`). Supported: keycrm, salesdrive, nethunt, zohocrm, pipedrive, onebox, hubspot, uspacy. |\n| **Real-time socket** | Live events to connected cabinet clients: new call record, active calls, online users. | See **Real-time**. |\n| **User notifications** | Push (FCM/APNs), e-mail and in-app notifications to the company's users about payments, expiring numbers, missed calls and callback requests. | `/notification-settings`, `/firebase-tokens`, `/user-notifications`. |\n| **Polling** | Everything else: `GET /statistics` (calls, with `filters[createdAt][$gte]=...`), `GET /missed-calls`, `GET /payments`, `GET /sms-messages`, `GET /orders`. | Available now, see the methods. |\n\nIncoming webhooks from payment and number providers are internal and are not part of the client API.\n\n## Proposed design (separate task, not implemented)\n\n1. **Subscription object** `webhook`: `url` (HTTPS), `events[]`, `secret`, `active`, managed by the owner via `GET/POST/PUT/DELETE /webhooks` and a test call `POST /webhooks/{id}/test`.\n2. **Events**: `call.started`, `call.answered`, `call.finished`, `call.missed`, `call.record.ready`, `sms.received`, `sms.sent`, `number.purchased`, `number.expiring`, `number.released`, `payment.succeeded`, `payment.failed`, `balance.low`, `subscription.renewed`, `subscription.payment_failed`, `subscription.closed`.\n3. **Payload**: `{ \"id\": \"evt_...\", \"type\": \"call.finished\", \"created_at\": \"...\", \"company_id\": 42, \"data\": { ... } }`, JSON, UTF-8.\n4. **Authenticity**: header `X-OVP-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, t + \".\" + body)>`; reject if `t` is older than 5 minutes.\n5. **Delivery**: queue (RabbitMQ) with retries 1 min, 5 min, 30 min, 2 h, 12 h; a 2xx response within 10 s counts as delivered; after 5 failures the subscription is paused and the owner is notified. Delivery log `GET /webhooks/{id}/deliveries`.\n6. **Source of events**: existing internal hooks (`telephony.*` events, payment and subscription lifecycles, SMS callbacks) already produce all of the above; the task is to add the fan-out and the management API.\n\n# Real-time\n\nA Socket.IO stream exists and is used by the web cabinet. It is limited to live state; it does not replace webhooks.\n\n* **Endpoint:** `wss://api.onevoiplanet.com/socket.io/` (Socket.IO v4, both `polling` and `websocket` transports).\n* **Authentication:** send the user JWT in the handshake header `Authorization: Bearer <JWT>` (with the browser client use `transportOptions.polling.extraHeaders`). API tokens are not accepted. Each connection joins the room of the user's company.\n* **Events from the server:**\n  * `activeCalls` — the current list of active calls of the company (array of call objects with `callid`, `from`, `to`, `callState`, `company`); sent when the state changes and in reply to the client's `activeCalls` request.\n  * `activeUsers` — number and list of online employees; in reply to the client's `activeUsers` request.\n  * `statisticCreate` — a new call record was saved (same object as `GET /statistics/{id}`).\n  * `statisticUpdate` — a call record was updated (for example, the record file became available).\n* **Events from the client:** emit `activeCalls` or `activeUsers` without payload to request a snapshot.\n* There is no event history and no acknowledgement; on reconnect request a snapshot and reconcile with `GET /statistics`.\n\nExample (browser):\n\n```js\nimport { io } from \"socket.io-client\";\nconst socket = io(\"https://api.onevoiplanet.com\", {\n  path: \"/socket.io/\",\n  transportOptions: { polling: { extraHeaders: { Authorization: \"Bearer \" + token } } },\n});\nsocket.on(\"connect\", () => socket.emit(\"activeCalls\"));\nsocket.on(\"activeCalls\", (calls) => console.log(calls));\nsocket.on(\"statisticCreate\", (call) => console.log(\"new call\", call.id));\n```\n\n# CRM and integrations\n\n## Connect your CRM (step by step)\n\n1. Create API credentials on the CRM side (API key, domain, or OAuth app) with permission to create calls/activities and look up contacts.\n2. Call `POST /companies/create-integration/{serviceName}` with those credentials; `serviceName` is one of `keycrm`, `salesdrive`, `nethunt`, `zohocrm`, `pipedrive`, `onebox`, `hubspot`, `uspacy`. For Zoho use `POST /zohocrm/generate-tokens` to exchange the OAuth code first; for OneBox use `POST /onebox/generate-token`.\n3. Check `GET /companies/my` → `integration[]` and set `active: true` with `POST /companies/edit-integration/{serviceName}/{id}` if needed.\n4. From now on every call of the company is pushed into the CRM in real time (see **Webhooks → What exists today**). The employee's SIP line is matched to the CRM user by the phone number / e-mail configured in the CRM.\n5. To disconnect: `DELETE /companies/delete-integration/{serviceName}/{id}`.\n\n## Issue a token for your own system\n\n1. Sign in as the company owner.\n2. Call `POST /crm-token/create-token` with `{ \"tokenName\": \"My CRM\" }` — the response contains the token value once; store it securely. The token lives 7 days and allows `GET /companies/my`, `GET /companies/getEmployees`, `POST /statistics/myStat`.\n3. Use it as `Authorization: Bearer <token>`.\n4. Revoke by deleting the record (`DELETE /crm-tokens/{id}`).\n\n**Note:** steps 2 and 4 are currently not enabled for client roles (the methods exist, the permission is not granted). Until they are enabled, request an API token from support@onevoiplanet.com.\n\n# Changelog\n\n| Version | Date | Changes |\n|---|---|---|\n| 3.1.0 | 2026-10-02 | First public specification of the client API (OpenAPI 3.1): Auth, Account & Company, Balance & Payments, Orders, Subscriptions, Numbers, SIP, Calls & Statistics, IVR & Groups, Callback widget, Contacts, SMS, eSIM, Integrations, Notifications. Documented limits, errors, idempotency rules, real-time stream. |\n\nBackward-incompatible changes will be announced here and by e-mail to company owners at least 30 days in advance.\n\n# Support\n\n* E-mail: **support@onevoiplanet.com** — include the request time (UTC), method, path and the `error` object from the response.\n* Terms of use: https://onevoiplanet.ua/terms-of-use\n* Privacy policy: https://onevoiplanet.ua/privacy-policy"
  },
  "servers": [
    {
      "url": "https://api.onevoiplanet.com/api",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Registration, login, password recovery and the current user profile."
    },
    {
      "name": "Account & Company",
      "description": "Company profile, employees, roles and audio files of the company."
    },
    {
      "name": "Balance & Payments",
      "description": "Balance, payment history, invoices (acts), saved cards and tariffs."
    },
    {
      "name": "Orders",
      "description": "Orders: buying numbers, talk packets, top-ups and other services. Orders are the only way money is charged."
    },
    {
      "name": "Subscriptions",
      "description": "Monthly subscriptions for numbers and services: renewal, auto-renew, restore."
    },
    {
      "name": "Numbers",
      "description": "Phone numbers of the company, the public number catalogue, external (own) numbers and identity verification required by regulators."
    },
    {
      "name": "SIP",
      "description": "SIP lines of employees, SIP trunks and SIP domains."
    },
    {
      "name": "Calls & Statistics",
      "description": "Call history and statistics, call records, missed calls, live call control."
    },
    {
      "name": "IVR & Groups",
      "description": "IVR menus, working hours and call groups (queues) of employees."
    },
    {
      "name": "Callback widget",
      "description": "Callback and recall widgets for your website: configuration, embed code, tickets and activity."
    },
    {
      "name": "Contacts",
      "description": "Company address book."
    },
    {
      "name": "SMS",
      "description": "SMS chats and messages on the company numbers."
    },
    {
      "name": "eSIM",
      "description": "eSIM catalogue (countries, regions, bundles) and the company's eSIMs."
    },
    {
      "name": "Integrations",
      "description": "CRM integrations and API tokens for external systems."
    },
    {
      "name": "Notifications",
      "description": "In-app notifications, notification settings and push tokens."
    }
  ],
  "x-tagGroups": [
    {
      "name": "Account",
      "tags": [
        "Auth",
        "Account & Company",
        "Notifications"
      ]
    },
    {
      "name": "Money",
      "tags": [
        "Balance & Payments",
        "Orders",
        "Subscriptions"
      ]
    },
    {
      "name": "Telephony",
      "tags": [
        "Numbers",
        "SIP",
        "Calls & Statistics",
        "IVR & Groups",
        "Callback widget",
        "Contacts",
        "SMS"
      ]
    },
    {
      "name": "Other services",
      "tags": [
        "eSIM",
        "Integrations"
      ]
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT from POST /auth/local (users) or an API token for external systems. Header: `Authorization: Bearer <token>`."
      }
    },
    "parameters": {
      "PageSize": {
        "name": "pagination[pageSize]",
        "in": "query",
        "required": false,
        "description": "Page size (1–100, default 25).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25,
          "example": 25
        }
      },
      "Page": {
        "name": "pagination[page]",
        "in": "query",
        "required": false,
        "description": "Page number, starting from 1.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1,
          "example": 1
        }
      },
      "Sort": {
        "name": "sort",
        "in": "query",
        "required": false,
        "description": "Sort field with direction, e.g. `createdAt:desc`. Several fields: `sort[0]=a:asc&sort[1]=b:desc`.",
        "schema": {
          "type": "string",
          "example": "createdAt:desc"
        }
      },
      "Filters": {
        "name": "filters",
        "in": "query",
        "required": false,
        "description": "Filters in Strapi syntax, e.g. `filters[status][$eq]=approved`, `filters[createdAt][$gte]=2026-01-01`. Operators: $eq, $ne, $lt, $lte, $gt, $gte, $in, $contains, $null, $notNull.",
        "schema": {
          "type": "string",
          "example": "filters[status][$eq]=approved"
        },
        "style": "deepObject",
        "explode": true
      },
      "Populate": {
        "name": "populate",
        "in": "query",
        "required": false,
        "description": "Relations to include: `populate=*` or `populate[0]=company&populate[1]=phones`.",
        "schema": {
          "type": "string",
          "example": "*"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Standard error envelope.",
        "properties": {
          "data": {
            "type": "null",
            "description": "Always null for errors."
          },
          "error": {
            "type": "object",
            "description": "Error details.",
            "properties": {
              "status": {
                "type": "integer",
                "description": "HTTP status code.",
                "example": 403
              },
              "name": {
                "type": "string",
                "description": "Error class: ValidationError, UnauthorizedError, ForbiddenError, NotFoundError, ApplicationError, PaymentRequiredError, RateLimitError.",
                "example": "ForbiddenError"
              },
              "message": {
                "type": "string",
                "description": "Human-readable message (English).",
                "example": "Forbidden"
              },
              "details": {
                "type": "object",
                "description": "Additional details, e.g. validation errors per field.",
                "example": {}
              }
            }
          }
        }
      },
      "Pagination": {
        "type": "object",
        "description": "Pagination block in `meta`.",
        "properties": {
          "page": {
            "type": "integer",
            "description": "Current page.",
            "example": 1
          },
          "pageSize": {
            "type": "integer",
            "description": "Page size.",
            "example": 25
          },
          "pageCount": {
            "type": "integer",
            "description": "Total pages.",
            "example": 4
          },
          "total": {
            "type": "integer",
            "description": "Total items.",
            "example": 87
          }
        }
      },
      "Meta": {
        "type": "object",
        "description": "Response metadata.",
        "properties": {
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "CompanyUser": {
        "type": "object",
        "description": "User (flat form). Private fields (password hash, reset tokens) are not meaningful for clients and must be ignored.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "User id.",
            "example": 43
          },
          "username": {
            "type": "string",
            "description": "Login.",
            "example": "jane"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "E-mail.",
            "example": "jane@example.com"
          },
          "provider": {
            "type": "string",
            "description": "Auth provider: local, google, apple.",
            "example": "local"
          },
          "confirmed": {
            "type": "boolean",
            "description": "E-mail confirmed.",
            "example": true
          },
          "blocked": {
            "type": "boolean",
            "description": "Blocked (cannot log in).",
            "example": false
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ],
            "description": "First name.",
            "example": "Jane"
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Last name.",
            "example": "Doe"
          },
          "middleName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Middle name.",
            "example": null
          },
          "type": {
            "type": "boolean",
            "description": "true — legal entity, false — individual (owner only).",
            "example": false
          },
          "birthDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Birth date.",
            "example": null
          },
          "defaultLang": {
            "type": "string",
            "description": "Language (en, ru, uk).",
            "example": "en"
          },
          "registration_platform": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mobile",
              "global",
              null
            ],
            "description": "Registration source.",
            "example": null
          },
          "referral_uid": {
            "type": "string",
            "description": "Referral code.",
            "example": "0a1b2c3d4e5f60718293a4b5c6d7e8f9"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-20T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-01-20T10:00:00.000Z"
          }
        }
      },
      "CompanyUserEntity": {
        "type": "object",
        "description": "User in Strapi envelope form.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "User id.",
            "example": 43
          },
          "attributes": {
            "$ref": "#/components/schemas/CompanyUser"
          }
        }
      },
      "CompanyFull": {
        "type": "object",
        "description": "Company with default relations (GET /companies/my).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Company id.",
            "example": 7
          },
          "attributes": {
            "type": "object",
            "description": "Company attributes and relations.",
            "properties": {
              "Name": {
                "type": "string",
                "description": "Company name.",
                "example": "Example LLC"
              },
              "YRGPO": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Registration number (EDRPOU).",
                "example": "12345678"
              },
              "validation": {
                "type": "string",
                "enum": [
                  "no",
                  "onProgress",
                  "yes"
                ],
                "description": "Identity verification status.",
                "example": "yes"
              },
              "tarificationDate": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date",
                "description": "Next tariff charge date.",
                "example": "2026-02-01"
              },
              "last_success_tarification": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date",
                "description": "Last successful tariff charge.",
                "example": "2026-01-01"
              },
              "missed_calls_timing": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Thresholds (seconds) for colouring missed calls: greenTiming, yellowTiming, redTiming.",
                "example": {
                  "redTiming": 1800,
                  "greenTiming": 60,
                  "yellowTiming": 300
                }
              },
              "demo_untill": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "End of demo period.",
                "example": null
              },
              "credit_limit": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Allowed negative balance.",
                "example": 5
              },
              "use_credit_for_service_payment": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Allow paying services from credit.",
                "example": false
              },
              "balance_modified_date": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Last balance change.",
                "example": "2026-01-20T10:18:11.057Z"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T06:32:20.297Z"
              },
              "tariff": {
                "type": "object",
                "description": "`{ data }` — tariff with `prices` (per currency, `price_by_field`: price, phone_channel_price, domain_cost, recall_widget_cost, callback_widget_cost, per_trunk_price) and `tariff_restrictions` (maxPhones, maxEmployees, maxDomains, ...). See Balance & Payments."
              },
              "phones": {
                "type": "object",
                "description": "`{ data: [] }` — phone numbers with routing relations (inSip, outSips, in_group, out_groups, in_ivr_line, out_ivr_lines, out_sip_lines, out_group_lines, sip_trunk), country_prefix and phone_prefix. See Numbers."
              },
              "register_ticket": {
                "type": "object",
                "description": "`{ data: [] }` — verification requests (POST /register-tickets/register)."
              },
              "users": {
                "type": "object",
                "description": "`{ data: [] }` — users of the company (same fields as CompanyUser)."
              },
              "domains": {
                "type": "object",
                "description": "`{ data: [] }` — SIP domains (id, name, payyed). See SIP."
              },
              "groups": {
                "type": "object",
                "description": "`{ data: [] }` — call groups. See IVR & Groups."
              },
              "recall_widgets": {
                "type": "object",
                "description": "`{ data: [] }` — recall widgets. See Callback widget."
              },
              "callback_widgets": {
                "type": "object",
                "description": "`{ data: [] }` — callback widgets. See Callback widget."
              },
              "ivrs": {
                "type": "object",
                "description": "`{ data: [] }` — IVR menus. See IVR & Groups."
              },
              "integration": {
                "type": "array",
                "items": {
                  "type": "object"
                },
                "description": "Dynamic zone of CRM integrations (`__component`: integration.keycrm, integration.salesdrive, integration.nethunt, integration.zohocrm, integration.pipedrive, integration.onebox, integration.hubspot, integration.uspacy). See Integrations."
              },
              "notification_setting": {
                "type": "object",
                "description": "`{ data }` — notification settings with notification_contacts. See Notifications."
              },
              "incoming_payment_system": {
                "type": "object",
                "description": "`{ data }` — payment acquirer for top-ups (name: wayforpay | stripe)."
              },
              "currency": {
                "type": "object",
                "description": "`{ data }` — billing currency (name_short, name_long, symbol, code)."
              },
              "sip_trunks": {
                "type": "object",
                "description": "`{ data: [] }` — SIP trunks. See SIP."
              }
            }
          }
        }
      },
      "CompanyBasicInfo": {
        "type": "object",
        "description": "Account summary (GET /companies/getBasicInfo). `id` is the user id.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "User id.",
            "example": 42
          },
          "attributes": {
            "type": "object",
            "description": "Summary fields.",
            "properties": {
              "firstName": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "First name.",
                "example": "John"
              },
              "lastName": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Last name.",
                "example": "Doe"
              },
              "middleName": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Middle name.",
                "example": null
              },
              "email": {
                "type": "string",
                "description": "E-mail.",
                "example": "owner@example.com"
              },
              "username": {
                "type": "string",
                "description": "Login.",
                "example": "owner"
              },
              "company": {
                "type": "object",
                "description": "Company: id, currency, tariff.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "Company id.",
                    "example": 7
                  },
                  "currency": {
                    "type": "object",
                    "description": "Billing currency.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Currency id.",
                        "example": 3
                      },
                      "name_short": {
                        "type": "string",
                        "description": "ISO code (UAH, USD).",
                        "example": "UAH"
                      },
                      "symbol": {
                        "type": "string",
                        "description": "Symbol.",
                        "example": "₴"
                      }
                    }
                  },
                  "tariff": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Current tariff (null if none).",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Tariff id.",
                        "example": 2
                      },
                      "Name": {
                        "type": "string",
                        "description": "Tariff name.",
                        "example": "Pro"
                      },
                      "prices": {
                        "type": "array",
                        "description": "Prices in the company currency.",
                        "items": {
                          "type": "object",
                          "description": "Price set.",
                          "properties": {
                            "id": {
                              "type": "integer",
                              "description": "Price set id.",
                              "example": 12
                            },
                            "price_by_field": {
                              "type": "array",
                              "description": "Named prices: price (monthly fee), phone_channel_price, domain_cost, recall_widget_cost, callback_widget_cost, per_trunk_price.",
                              "items": {
                                "type": "object",
                                "description": "Named price.",
                                "properties": {
                                  "id": {
                                    "type": "integer",
                                    "description": "Id.",
                                    "example": 1
                                  },
                                  "name": {
                                    "type": "string",
                                    "description": "Price name.",
                                    "example": "price"
                                  },
                                  "price": {
                                    "type": "number",
                                    "description": "Amount in the company currency.",
                                    "example": 629
                                  },
                                  "isPriceStatic": {
                                    "type": "boolean",
                                    "description": "Fixed price flag.",
                                    "example": false
                                  }
                                }
                              }
                            }
                          }
                        }
                      },
                      "tariff_restrictions": {
                        "type": "array",
                        "description": "Limits.",
                        "items": {
                          "type": "object",
                          "description": "Limit set.",
                          "properties": {
                            "id": {
                              "type": "integer",
                              "description": "Id.",
                              "example": 2
                            },
                            "maxPhones": {
                              "type": "integer",
                              "description": "Max phone numbers.",
                              "example": 50
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "sip": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "User's SIP line with its outgoing numbers.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "SIP line id.",
                    "example": 15
                  },
                  "out_phone_lines": {
                    "type": "array",
                    "description": "Numbers the line may call from.",
                    "items": {
                      "type": "object",
                      "description": "Phone number.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Phone id.",
                          "example": 35
                        },
                        "fullnumber": {
                          "type": "string",
                          "description": "Full number without +.",
                          "example": "380441234567"
                        }
                      }
                    }
                  }
                }
              },
              "role": {
                "type": "object",
                "description": "User role.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "Role id.",
                    "example": 4
                  },
                  "name": {
                    "type": "string",
                    "description": "Name.",
                    "example": "Company owner (approved)"
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Description.",
                    "example": "After company approved, tariff needed"
                  },
                  "type": {
                    "type": "string",
                    "description": "Role code.",
                    "example": "company_owner_approved"
                  }
                }
              },
              "effective_payment_acquirer": {
                "type": "string",
                "enum": [
                  "wayforpay",
                  "stripe"
                ],
                "description": "Payment acquirer to show, by client IP geolocation.",
                "example": "wayforpay"
              },
              "effective_country": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Country code of the client IP.",
                "example": "UA"
              }
            }
          }
        }
      },
      "CompanyVerificationTicket": {
        "type": "object",
        "description": "Verification request.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Ticket id.",
            "example": 12
          },
          "attributes": {
            "type": "object",
            "description": "Submitted data (as given in `data`).",
            "properties": {
              "firstName": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "First name.",
                "example": "John"
              },
              "lastName": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Last name.",
                "example": "Doe"
              },
              "birthDate": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Birth date DD/MM/YYYY.",
                "example": "20/05/1990"
              },
              "passportSeries": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Passport series.",
                "example": "AB"
              },
              "passportNumber": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Passport number.",
                "example": "123456"
              },
              "whoIssued": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Issuing authority.",
                "example": "1234"
              },
              "dateRelease": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Issue date DD/MM/YYYY.",
                "example": "10/06/2015"
              },
              "IdPassportNumber": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "ID-card number (if ID card).",
                "example": null
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Contact phone.",
                "example": "+380441234567"
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Contact e-mail.",
                "example": "owner@example.com"
              },
              "country": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Country code.",
                "example": "UA"
              },
              "zip": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Postal code.",
                "example": "01001"
              },
              "state": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Region.",
                "example": null
              },
              "city": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "City.",
                "example": "Kyiv"
              },
              "street": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Street.",
                "example": "Khreshchatyk"
              },
              "house": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "House.",
                "example": "1"
              },
              "apartment": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Apartment.",
                "example": "2"
              },
              "YRGPO": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Company registration number (copied from the company).",
                "example": ""
              },
              "ticket_status": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "VERIFICATION_STATUS_CHANGED",
                  "VERIFICATION_COMPLETED",
                  "DOCUMENT_EXPIRED",
                  "DATABASE_SCREENING",
                  null
                ],
                "description": "Review status set by the verification provider.",
                "example": null
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-02-02T09:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-02T09:00:00.000Z"
              }
            }
          }
        }
      },
      "CompanyAudioFlat": {
        "type": "object",
        "description": "Audio file (flat form).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Audio id.",
            "example": 156
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "Greeting"
          },
          "type": {
            "type": "string",
            "enum": [
              "sound",
              "moh"
            ],
            "description": "sound — announcement, moh — music on hold.",
            "example": "sound"
          },
          "category": {
            "type": "string",
            "enum": [
              "base",
              "additional"
            ],
            "description": "base — system library, additional — company file.",
            "example": "additional"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Path of the converted WAV (null until converted).",
            "example": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav"
          },
          "size": {
            "type": [
              "number",
              "null"
            ],
            "description": "File size (kilobytes).",
            "example": 154.69
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-20T12:08:36.711Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-01-20T12:08:36.711Z"
          }
        }
      },
      "CompanyAudio": {
        "type": "object",
        "description": "Audio file (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Audio id.",
            "example": 156
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Display name.",
                "example": "Greeting"
              },
              "type": {
                "type": "string",
                "enum": [
                  "sound",
                  "moh"
                ],
                "description": "sound — announcement, moh — music on hold.",
                "example": "sound"
              },
              "category": {
                "type": "string",
                "enum": [
                  "base",
                  "additional"
                ],
                "description": "base — system library, additional — company file.",
                "example": "additional"
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Path of the converted WAV (null until converted).",
                "example": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav"
              },
              "size": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "File size (kilobytes).",
                "example": 154.69
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-20T12:08:36.711Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-20T12:08:36.711Z"
              },
              "file": {
                "type": "object",
                "description": "`{ data }` — stored file (Strapi upload): name, ext, mime, size (KB), url, hash, provider.",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "File entity.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "File id.",
                        "example": 1001
                      },
                      "attributes": {
                        "type": "object",
                        "description": "File attributes.",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "File name.",
                            "example": "greeting.wav"
                          },
                          "ext": {
                            "type": "string",
                            "description": "Extension.",
                            "example": ".wav"
                          },
                          "mime": {
                            "type": "string",
                            "description": "MIME type.",
                            "example": "audio/mpeg"
                          },
                          "size": {
                            "type": "number",
                            "description": "Size, KB.",
                            "example": 154.69
                          },
                          "url": {
                            "type": "string",
                            "description": "Relative URL (prefix with the API host without /api).",
                            "example": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav"
                          },
                          "hash": {
                            "type": "string",
                            "description": "Storage hash.",
                            "example": "greeting_wav_a1b2c3"
                          },
                          "provider": {
                            "type": "string",
                            "description": "Storage provider.",
                            "example": "local"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Created at.",
                            "example": "2026-01-20T12:08:30.000Z"
                          },
                          "updatedAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Updated at.",
                            "example": "2026-01-20T12:08:36.000Z"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "owner": {
                "type": "object",
                "description": "`{ data }` — user who uploaded the file (company files only).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "User entity (fields as in CompanyUser).",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "User id.",
                        "example": 42
                      },
                      "attributes": {
                        "$ref": "#/components/schemas/CompanyUser"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "AuthUser": {
        "type": "object",
        "description": "Public user profile (private fields such as the password hash are removed).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "User id.",
            "example": 42
          },
          "username": {
            "type": "string",
            "description": "Login.",
            "example": "owner"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "E-mail.",
            "example": "owner@example.com"
          },
          "provider": {
            "type": "string",
            "description": "Auth provider: local, google, apple.",
            "example": "local"
          },
          "confirmed": {
            "type": "boolean",
            "description": "E-mail confirmed.",
            "example": true
          },
          "blocked": {
            "type": "boolean",
            "description": "Account blocked (login impossible).",
            "example": false
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ],
            "description": "First name.",
            "example": "John"
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Last name.",
            "example": "Doe"
          },
          "middleName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Middle name.",
            "example": null
          },
          "type": {
            "type": "boolean",
            "description": "true — legal entity, false — individual.",
            "example": false
          },
          "birthDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Birth date.",
            "example": null
          },
          "defaultLang": {
            "type": "string",
            "description": "Interface / notification language (en, ru, uk).",
            "example": "en"
          },
          "registration_platform": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mobile",
              "global",
              null
            ],
            "description": "Where the user registered: mobile app or web (global).",
            "example": "global"
          },
          "referral_uid": {
            "type": "string",
            "description": "Referral code of this user (pass it as `ref` on registration of invited users).",
            "example": "f5436f2e0178004a599a96209c746f78"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at (ISO-8601, UTC).",
            "example": "2026-02-01T12:00:00.000Z"
          }
        }
      },
      "AuthSession": {
        "type": "object",
        "description": "Login result: JWT + user.",
        "properties": {
          "jwt": {
            "type": "string",
            "description": "Bearer token, valid 30 days. Absent on registration when e-mail confirmation is required.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDJ9.sig"
          },
          "user": {
            "$ref": "#/components/schemas/AuthUser"
          }
        }
      },
      "AuthRole": {
        "type": "object",
        "description": "User role.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Role id.",
            "example": 4
          },
          "name": {
            "type": "string",
            "description": "Role display name.",
            "example": "Company owner (approved)"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Role description.",
            "example": "After company approved, tariff needed"
          },
          "type": {
            "type": "string",
            "description": "Role code: company_owner, company_owner_approved, superwisor, finance, authenticated (employee).",
            "example": "company_owner_approved"
          },
          "permissions": {
            "type": "array",
            "description": "Granted API actions (present unless `populate[role]=false`). Internal key fields are omitted here.",
            "items": {
              "type": "object",
              "description": "Permission.",
              "properties": {
                "id": {
                  "type": "integer",
                  "description": "Permission id.",
                  "example": 56
                },
                "action": {
                  "type": "string",
                  "description": "Action code, e.g. api::company.company.my.",
                  "example": "api::company.company.my"
                }
              }
            }
          }
        }
      },
      "AuthMe": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AuthUser"
          },
          {
            "type": "object",
            "description": "Relations populated by default in /users/me.",
            "properties": {
              "role": {
                "$ref": "#/components/schemas/AuthRole"
              },
              "company": {
                "type": "object",
                "description": "User's company (short form; full form — GET /companies/my).",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "Company id.",
                    "example": 7
                  },
                  "Name": {
                    "type": "string",
                    "description": "Company name.",
                    "example": "Example LLC"
                  },
                  "YRGPO": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Registration number (EDRPOU).",
                    "example": ""
                  },
                  "validation": {
                    "type": "string",
                    "enum": [
                      "no",
                      "onProgress",
                      "yes"
                    ],
                    "description": "Identity verification status of the company.",
                    "example": "yes"
                  },
                  "tarificationDate": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date",
                    "description": "Next tariff charge date.",
                    "example": "2026-02-01"
                  },
                  "last_success_tarification": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date",
                    "description": "Last successful tariff charge.",
                    "example": null
                  },
                  "missed_calls_timing": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Thresholds (seconds) for colouring missed calls: greenTiming, yellowTiming, redTiming.",
                    "example": {
                      "redTiming": 1800,
                      "greenTiming": 60,
                      "yellowTiming": 300
                    }
                  },
                  "demo_untill": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "End of demo period, if any.",
                    "example": null
                  },
                  "credit_limit": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "Allowed negative balance (credit).",
                    "example": 5
                  },
                  "use_credit_for_service_payment": {
                    "type": [
                      "boolean",
                      "null"
                    ],
                    "description": "Allow paying services from credit.",
                    "example": false
                  },
                  "balance_modified_date": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "Last balance change.",
                    "example": "2026-02-01T10:00:00.000Z"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Created at.",
                    "example": "2026-01-15T10:00:00.000Z"
                  },
                  "updatedAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Updated at.",
                    "example": "2026-02-01T10:00:00.000Z"
                  }
                }
              },
              "sip": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "User's SIP line (null if none).",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "SIP line id.",
                    "example": 15
                  },
                  "number": {
                    "type": "string",
                    "description": "Internal SIP number (3 digits).",
                    "example": "001"
                  },
                  "password": {
                    "type": "string",
                    "description": "SIP password (returned by the server; shown masked here).",
                    "example": "********"
                  },
                  "record_call": {
                    "type": "boolean",
                    "description": "Record calls of this line.",
                    "example": true
                  },
                  "call_forwarding": {
                    "type": "string",
                    "description": "Call forwarding mode.",
                    "example": "off"
                  },
                  "redirectActivity": {
                    "type": [
                      "boolean",
                      "null"
                    ],
                    "description": "Redirect enabled.",
                    "example": null
                  },
                  "redirectTo": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Redirect target number.",
                    "example": null
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Created at.",
                    "example": "2026-01-15T10:00:00.000Z"
                  },
                  "updatedAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Updated at.",
                    "example": "2026-01-15T10:00:00.000Z"
                  },
                  "domain": {
                    "type": "object",
                    "description": "SIP domain of the line.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Domain id.",
                        "example": 3
                      },
                      "name": {
                        "type": "string",
                        "description": "Domain name (the SIP account is `<number>domain<name>`).",
                        "example": "examplellc"
                      },
                      "createdAt": {
                        "type": "string",
                        "format": "date-time",
                        "description": "Created at.",
                        "example": "2026-01-15T10:00:00.000Z"
                      },
                      "updatedAt": {
                        "type": "string",
                        "format": "date-time",
                        "description": "Updated at.",
                        "example": "2026-01-15T10:00:00.000Z"
                      }
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "AuthGeoIp": {
        "type": [
          "object",
          "null"
        ],
        "description": "GeoIP record (geoip-lite).",
        "properties": {
          "range": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "IP range [from, to] as integers.",
            "example": [
              1834458112,
              1834459135
            ]
          },
          "country": {
            "type": "string",
            "description": "ISO-3166-1 alpha-2 country code.",
            "example": "UA"
          },
          "region": {
            "type": "string",
            "description": "Region code.",
            "example": "30"
          },
          "eu": {
            "type": "string",
            "description": "\"1\" if in the EU, else \"0\".",
            "example": "0"
          },
          "timezone": {
            "type": "string",
            "description": "IANA time zone.",
            "example": "Europe/Kyiv"
          },
          "city": {
            "type": "string",
            "description": "City.",
            "example": "Kyiv"
          },
          "ll": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "description": "[latitude, longitude].",
            "example": [
              50.4547,
              30.5238
            ]
          },
          "metro": {
            "type": "integer",
            "description": "Metro code (US only).",
            "example": 0
          },
          "area": {
            "type": "integer",
            "description": "Accuracy radius, km.",
            "example": 20
          }
        }
      },
      "BalanceInfo": {
        "type": "object",
        "description": "Balance of the company.",
        "properties": {
          "balance": {
            "type": "number",
            "description": "Current balance in the company currency (may be negative within the credit limit).",
            "example": 99.98
          },
          "limit": {
            "type": "number",
            "description": "Credit limit: how far below zero the balance may go. 0 — no credit.",
            "example": 5
          }
        }
      },
      "Payment": {
        "type": "object",
        "description": "Payment (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Payment id.",
            "example": 101
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "new",
                  "pending",
                  "approved",
                  "declined",
                  "refunded"
                ],
                "description": "Payment status. `approved` — money moved; `declined` — rejected (e.g. not enough balance); `new`/`pending` — in progress.",
                "example": "approved"
              },
              "amount": {
                "type": "number",
                "description": "Amount in the payment currency. Negative — charge from the balance, positive — credit to the balance.",
                "example": -5.99
              },
              "commentary": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Purpose/comment. Technical comments look like `Payment: <id>`.",
                "example": "Payment: 1790910007661138"
              },
              "payment_result": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Raw result of the payment provider (balance core or card gateway) without secrets. For declined balance charges contains `{ \"Result\": \"Not enough money on balance\" }`. Structure depends on the provider; use `status` for logic.",
                "additionalProperties": true,
                "example": {
                  "Result": "Not enough money on balance"
                }
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at (ISO-8601, UTC).",
                "example": "2026-09-01T03:00:12.110Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-09-01T03:00:12.536Z"
              },
              "payment_system": {
                "type": "object",
                "description": "Payment system (with `populate`). `name` is a display name; balance payments are the internal account.",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Relation data.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Payment system id.",
                        "example": 2
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Attributes.",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "Display name.",
                            "example": "Balance"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "currency": {
                "type": "object",
                "description": "Currency (with `populate`).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Relation data.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Currency id.",
                        "example": 1
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Attributes.",
                        "properties": {
                          "name_short": {
                            "type": "string",
                            "description": "ISO code.",
                            "example": "USD"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "order": {
                "type": "object",
                "description": "Related order (with `populate`); null for subscription/manual payments.",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Relation data.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Order id.",
                        "example": 42
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Attributes.",
                        "properties": {
                          "orderId": {
                            "type": "string",
                            "description": "Order number.",
                            "example": "1788231620358546"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "subscription": {
                "type": "object",
                "description": "Related subscription (with `populate`); set for auto-renewal charges.",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Relation data.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Subscription id.",
                        "example": 12
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "PaymentActBody": {
        "type": "object",
        "description": "Act content for a period.",
        "properties": {
          "date": {
            "type": "object",
            "description": "Period.",
            "properties": {
              "from": {
                "type": "string",
                "format": "date",
                "description": "Start.",
                "example": "2026-09-01"
              },
              "to": {
                "type": "string",
                "format": "date",
                "description": "End.",
                "example": "2026-09-30"
              }
            }
          },
          "dataJSON": {
            "type": "object",
            "description": "Charged items grouped by service. Each item carries the object snapshot plus `calculatedMonthPrice` (charged amount).",
            "properties": {
              "total": {
                "type": "number",
                "description": "Total of all items and calls.",
                "example": 8.25
              },
              "refill": {
                "type": "number",
                "description": "Top-ups (always 0 in the act).",
                "example": 0
              },
              "tariff": {
                "type": "array",
                "description": "Tariff charges.",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Tariff snapshot."
                }
              },
              "phone": {
                "type": "array",
                "description": "Number charges (monthly fee / setup).",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Number snapshot."
                }
              },
              "domain": {
                "type": "array",
                "description": "Domain charges.",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Domain snapshot."
                }
              },
              "esim": {
                "type": "array",
                "description": "eSIM purchases.",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "eSIM snapshot."
                }
              },
              "recall_widget": {
                "type": "array",
                "description": "Recall widget charges.",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Widget snapshot."
                }
              },
              "callback_widget": {
                "type": "array",
                "description": "Callback widget charges.",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Widget snapshot."
                }
              },
              "sip-trunk": {
                "type": "array",
                "description": "SIP trunk charges.",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Trunk snapshot."
                }
              },
              "payment": {
                "type": "array",
                "description": "Other charges without an order or subscription: `{ description, price }`.",
                "items": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Payment line."
                }
              },
              "phone_calls": {
                "type": "array",
                "description": "Call costs per number: `[number, totalSeconds, cost]`.",
                "items": {
                  "type": "array",
                  "description": "Triple `[number, seconds, cost]`.",
                  "items": {
                    "description": "Number (string), seconds (integer) or cost (number)."
                  }
                },
                "example": [
                  [
                    "380441234567",
                    339,
                    2.26
                  ]
                ]
              }
            }
          }
        }
      },
      "PaymentAct": {
        "type": "object",
        "description": "Saved act (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Act id.",
            "example": 15
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Act name (Ukrainian, by month).",
                "example": "Акт рахунок за Квітень 2026"
              },
              "amount": {
                "type": "number",
                "description": "Act total in the company currency.",
                "example": 2.26
              },
              "body": {
                "type": "object",
                "description": "Act content.",
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/PaymentActBody"
                  }
                }
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-05-01T05:00:04.431Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-05-01T05:00:04.431Z"
              }
            }
          }
        }
      },
      "PaymentActFlat": {
        "type": "object",
        "description": "Act as a flat object (returned by generateAct).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Act id.",
            "example": 16
          },
          "name": {
            "type": "string",
            "description": "Act name.",
            "example": "Акт рахунок за Вересень 2026"
          },
          "amount": {
            "type": "number",
            "description": "Act total.",
            "example": 8.25
          },
          "body": {
            "type": "object",
            "description": "Act content.",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/PaymentActBody"
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-10-01T05:00:04.431Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-10-01T05:00:04.431Z"
          }
        }
      },
      "PaymentActListMeta": {
        "type": "object",
        "description": "List metadata with totals.",
        "properties": {
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          },
          "totals": {
            "$ref": "#/components/schemas/MoneyTotals"
          }
        }
      },
      "MoneyTotals": {
        "type": "object",
        "description": "Sum over the whole filtered set.",
        "properties": {
          "amount": {
            "type": "number",
            "description": "Total amount.",
            "example": 8.46
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Company currency code.",
            "example": "USD"
          }
        }
      },
      "StripePaymentSheet": {
        "type": "object",
        "description": "Parameters for Stripe PaymentSheet.",
        "properties": {
          "paymentIntent": {
            "type": "string",
            "description": "PaymentIntent client secret.",
            "example": "pi_3Q********_secret_********"
          },
          "ephemeralKey": {
            "type": "string",
            "description": "Ephemeral key secret for the customer.",
            "example": "ek_test_********"
          },
          "customer": {
            "type": "string",
            "description": "Stripe customer id.",
            "example": "cus_********"
          },
          "publishableKey": {
            "type": "string",
            "description": "Stripe publishable key.",
            "example": "pk_live_********"
          },
          "amount": {
            "type": "integer",
            "description": "Amount in USD cents.",
            "example": 599
          },
          "currency": {
            "type": "string",
            "description": "Always `usd`.",
            "example": "usd"
          },
          "recurring": {
            "type": [
              "object",
              "null"
            ],
            "description": "Recurring disclosure for Apple/Google Pay when the order contains a monthly part (number, tariff); null for one-off orders.",
            "properties": {
              "intervalUnit": {
                "type": "string",
                "description": "Always `month`.",
                "example": "month"
              },
              "intervalCount": {
                "type": "integer",
                "description": "Always 1.",
                "example": 1
              },
              "amount": {
                "type": "number",
                "description": "Monthly amount in USD (major units).",
                "example": 5.99
              },
              "currency": {
                "type": "string",
                "description": "Always `usd`.",
                "example": "usd"
              },
              "label": {
                "type": "string",
                "description": "What is renewed.",
                "example": "номер +15551234567"
              },
              "description": {
                "type": "string",
                "description": "Human-readable description.",
                "example": "Ежемесячное продление: номер +15551234567"
              },
              "managementUrl": {
                "type": "string",
                "description": "Where the user manages the subscription.",
                "example": "https://cabinet.example.com/call-processing-numbers"
              }
            }
          }
        }
      },
      "StripeCard": {
        "type": "object",
        "description": "Saved card.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stripe payment method id.",
            "example": "pm_1Q********"
          },
          "brand": {
            "type": "string",
            "description": "Card brand: visa, mastercard, ...",
            "example": "visa"
          },
          "last4": {
            "type": "string",
            "description": "Last 4 digits.",
            "example": "4242"
          },
          "expMonth": {
            "type": "integer",
            "description": "Expiry month.",
            "example": 12
          },
          "expYear": {
            "type": "integer",
            "description": "Expiry year.",
            "example": 2028
          },
          "isDefault": {
            "type": "boolean",
            "description": "Default card for auto-renewals.",
            "example": true
          },
          "isExpired": {
            "type": "boolean",
            "description": "Card has expired.",
            "example": false
          }
        }
      },
      "StripeDefaultCard": {
        "type": "object",
        "description": "Default card or absence of cards.",
        "properties": {
          "hasCard": {
            "type": "boolean",
            "description": "false — no saved card; other fields are absent.",
            "example": true
          },
          "brand": {
            "type": "string",
            "description": "Card brand.",
            "example": "visa"
          },
          "last4": {
            "type": "string",
            "description": "Last 4 digits.",
            "example": "4242"
          },
          "expMonth": {
            "type": "integer",
            "description": "Expiry month.",
            "example": 12
          },
          "expYear": {
            "type": "integer",
            "description": "Expiry year.",
            "example": 2028
          },
          "isDefault": {
            "type": "boolean",
            "description": "true — the Stripe default card; false — no default set, the first card is shown.",
            "example": true
          },
          "isExpired": {
            "type": "boolean",
            "description": "Card has expired.",
            "example": false
          }
        }
      },
      "Currency": {
        "type": "object",
        "description": "Currency (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Currency id.",
            "example": 1
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "name_short": {
                "type": "string",
                "description": "ISO code.",
                "example": "USD"
              },
              "name_long": {
                "type": "string",
                "description": "Full name.",
                "example": "US Dollar"
              },
              "symbol": {
                "type": "string",
                "description": "Symbol.",
                "example": "$"
              },
              "prefix": {
                "type": "string",
                "description": "Prefix for formatting.",
                "example": ""
              },
              "suffix": {
                "type": "string",
                "description": "Suffix for formatting.",
                "example": ""
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2024-04-23T20:18:29.106Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2024-04-23T20:18:29.106Z"
              }
            }
          }
        }
      },
      "PriceByField": {
        "type": "object",
        "description": "One price line of a price set.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Line id.",
            "example": 11
          },
          "name": {
            "type": "string",
            "enum": [
              "price",
              "setup_price",
              "phone_channel_price",
              "domain_cost",
              "recall_widget_cost",
              "callback_widget_cost",
              "per_trunk_price",
              "sms_incoming_price",
              "sms_outgoing_price"
            ],
            "description": "What the price is for: `price` — monthly fee, `setup_price` — one-off setup, others — per extra item.",
            "example": "price"
          },
          "price": {
            "type": "number",
            "description": "Price in the currency of the price set.",
            "example": 5.99
          },
          "isPriceStatic": {
            "type": "boolean",
            "description": "Internal flag; ignore.",
            "example": false
          }
        }
      },
      "PriceSet": {
        "type": "object",
        "description": "Prices in one currency (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Price set id.",
            "example": 3
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "price_by_field": {
                "type": "array",
                "description": "Price lines.",
                "items": {
                  "$ref": "#/components/schemas/PriceByField"
                }
              },
              "currency": {
                "type": "object",
                "description": "Currency of the set (with populate).",
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/Currency"
                  }
                }
              }
            }
          }
        }
      },
      "Tariff": {
        "type": "object",
        "description": "Tariff (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Tariff id.",
            "example": 1
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "Name": {
                "type": "string",
                "description": "Tariff name.",
                "example": "Basic"
              },
              "custom": {
                "type": "boolean",
                "description": "Individual tariff created for a specific company.",
                "example": false
              },
              "isAi": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "AI tariff (limits are not enforced).",
                "example": false
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2024-02-16T14:04:57.675Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-05-06T09:24:06.362Z"
              },
              "tariff_restrictions": {
                "type": "object",
                "description": "Limits (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Limit sets (usually one).",
                    "items": {
                      "$ref": "#/components/schemas/TariffRestriction"
                    }
                  }
                }
              },
              "prices": {
                "type": "object",
                "description": "Prices per currency (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Price sets.",
                    "items": {
                      "$ref": "#/components/schemas/PriceSet"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "TariffRestriction": {
        "type": "object",
        "description": "Tariff limits (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Limit set id.",
            "example": 1
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "maxPhones": {
                "type": "integer",
                "description": "Maximum phone numbers.",
                "example": 4
              },
              "maxEmployees": {
                "type": "integer",
                "description": "Maximum employees (users).",
                "example": 10
              },
              "maxDomains": {
                "type": "integer",
                "description": "Maximum SIP domains.",
                "example": 3
              },
              "maxIVRSubVoutines": {
                "type": "integer",
                "description": "Maximum nested IVR menus.",
                "example": 1
              },
              "max_callback_widgets": {
                "type": "integer",
                "description": "Maximum callback widgets.",
                "example": 1
              },
              "maxRecallWidgets": {
                "type": "integer",
                "description": "Maximum recall widgets.",
                "example": 1
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2023-01-30T13:41:14.528Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2024-08-15T10:27:28.972Z"
              }
            }
          }
        }
      },
      "FinanceFeedRow": {
        "type": "object",
        "description": "Row of a finance feed.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Row id: `o-<orderId>` for orders, `p-<paymentId>` for payments.",
            "example": "o-42"
          },
          "refId": {
            "type": "integer",
            "description": "Id of the order or payment.",
            "example": 42
          },
          "source": {
            "type": "string",
            "enum": [
              "order",
              "payment"
            ],
            "description": "Row source.",
            "example": "order"
          },
          "kind": {
            "type": "string",
            "enum": [
              "order",
              "topup",
              "subscription",
              "sms",
              "charge",
              "refund",
              "manual"
            ],
            "description": "Row type: `order` — purchase, `topup` — balance top-up, `subscription` — auto-renewal, `sms`, `charge` — other charge, `refund`, `manual` — manual credit.",
            "example": "order"
          },
          "description": {
            "type": "string",
            "description": "Human-readable purpose (Russian).",
            "example": "Покупка номера"
          },
          "descriptionKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "i18n key: topup, tariff, phone, phoneRenew, esim, domain, widgetRecall, widgetCallback, sipTrunk, order, renewPhone, renewExternalPhone, renewTariff, renewDomain, renewSipTrunk, renewWidget, renewSubscription, sms, balanceCharge, balanceCredit; null for free-text.",
            "example": "phone"
          },
          "descriptionParams": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Parameters for the key (e.g. `{ name }` for tariff, `{ n }` for order).",
            "example": null
          },
          "amount": {
            "type": "number",
            "description": "Absolute amount.",
            "example": 6.49
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "pending",
              "frozen",
              "approved",
              "declined",
              "refunded"
            ],
            "description": "Status of the order or payment.",
            "example": "approved"
          },
          "paymentMethod": {
            "type": [
              "string",
              "null"
            ],
            "description": "Payment system code: `mc-billing` (balance), `stripe`, `wayforpay`, `paypal`.",
            "example": "stripe"
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "Date of the movement.",
            "example": "2026-09-10T08:33:05.765Z"
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Currency code.",
            "example": "USD"
          }
        }
      },
      "FinanceFeedResponse": {
        "type": "object",
        "description": "Finance feed page.",
        "properties": {
          "data": {
            "type": "array",
            "description": "Rows.",
            "items": {
              "type": "object",
              "description": "Row envelope.",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Row id.",
                  "example": "o-42"
                },
                "attributes": {
                  "$ref": "#/components/schemas/FinanceFeedRow"
                }
              }
            }
          },
          "meta": {
            "type": "object",
            "description": "Pagination and totals.",
            "properties": {
              "pagination": {
                "$ref": "#/components/schemas/Pagination"
              },
              "totals": {
                "$ref": "#/components/schemas/MoneyTotals"
              }
            }
          }
        }
      },
      "CallbackWidgetRedirect": {
        "type": "object",
        "description": "Call destination (component `redirect`). In requests pass only the id: `{ \"user\": 7 }` or `{ \"group\": 2 }` (recall widgets also accept `{ \"ivr\": 1 }`). In responses the relations are returned as Strapi relation objects when populated.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (response only).",
            "example": 3
          },
          "user": {
            "description": "Employee who receives the call. Request: user id. Response (populated): `{ data: { id, attributes } }` or `{ data: null }`.",
            "example": 7
          },
          "group": {
            "description": "Call group (queue) that receives the call. Request: group id. Response (populated): `{ data: { id, attributes } }` or `{ data: null }`.",
            "example": 2
          },
          "ivr": {
            "description": "IVR menu (recall widgets only). Request: ivr id. Response (populated): relation object.",
            "example": 1
          },
          "call_end": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Not used by widgets.",
            "example": null
          },
          "extension": {
            "type": [
              "string",
              "null"
            ],
            "description": "Not used by widgets.",
            "example": null
          }
        }
      },
      "CallbackWidgetFormOpen": {
        "type": "object",
        "description": "One widget form opening (component `form_opened_counter`), recorded by the widget script.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id.",
            "example": 1
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "When the form was opened.",
            "example": "2026-01-20T14:09:30.000Z"
          }
        }
      },
      "CallbackWidgetFirstTimeContact": {
        "type": "object",
        "description": "Unique phone number with the date of its first ticket in the period.",
        "properties": {
          "contact_number": {
            "type": "string",
            "description": "Visitor phone number.",
            "example": "+380441234567"
          },
          "first_created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Date of the first ticket of this number in the period.",
            "example": "2026-01-05T09:12:48.546Z"
          }
        }
      },
      "CallbackWidgetAttributes": {
        "type": "object",
        "description": "Callback widget attributes.",
        "properties": {
          "sitename": {
            "type": "string",
            "description": "Domain of the site where the widget is installed, without protocol and `www.` (unique). Public ticket requests must come from this domain.",
            "example": "example.com"
          },
          "activity": {
            "type": "boolean",
            "description": "Widget is enabled.",
            "example": true
          },
          "widget_name": {
            "type": "string",
            "maxLength": 100,
            "description": "Widget name (shown in the cabinet and in notifications).",
            "example": "Main site"
          },
          "work_time": {
            "type": [
              "object",
              "null"
            ],
            "description": "Working hours by weekday (`mon`..`sun`), value `HH:mm-HH:mm`. Outside these hours the widget offers to schedule a call.",
            "additionalProperties": {
              "type": "string",
              "description": "Time range `HH:mm-HH:mm`.",
              "example": "09:00-18:00"
            },
            "example": {
              "mon": "09:00-18:00",
              "tue": "09:00-18:00",
              "wed": "09:00-18:00",
              "thu": "09:00-18:00",
              "fri": "09:00-18:00"
            }
          },
          "active_on_exit": {
            "type": "boolean",
            "description": "Show the form when the visitor is about to leave the page (exit intent).",
            "example": true
          },
          "hide_on_weekend": {
            "type": "boolean",
            "description": "Hide the widget on days without `work_time`.",
            "example": false
          },
          "widget_hash": {
            "type": "string",
            "description": "Public widget identifier (UUID) used in the embed code; generated by the server, read-only.",
            "example": "5c5822c3-0356-4230-b855-0879ff937e3d"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at (ISO-8601, UTC).",
            "example": "2026-02-01T12:00:00.000Z"
          },
          "redirect": {
            "$ref": "#/components/schemas/CallbackWidgetRedirect"
          },
          "form_opened_counter": {
            "type": "array",
            "description": "Form openings recorded by the widget (populate `form_opened_counter`).",
            "items": {
              "$ref": "#/components/schemas/CallbackWidgetFormOpen"
            }
          },
          "callback_widget_design": {
            "type": "object",
            "description": "Design of the widget (populate).",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/CallbackWidgetDesign"
              }
            }
          },
          "callback_widget_activity": {
            "type": "object",
            "description": "Activity settings of the widget (populate).",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/CallbackWidgetActivity"
              }
            }
          },
          "callback_widget_tickets": {
            "type": "object",
            "description": "Tickets of the widget (populate).",
            "properties": {
              "data": {
                "type": "array",
                "description": "Tickets.",
                "items": {
                  "$ref": "#/components/schemas/CallbackWidgetTicket"
                }
              }
            }
          },
          "statistics": {
            "type": "object",
            "description": "Call statistics records of the widget callbacks (populate; see Calls & Statistics).",
            "properties": {
              "data": {
                "type": "array",
                "description": "Call records.",
                "items": {
                  "type": "object",
                  "description": "Call record (`{ id, attributes }`)."
                }
              }
            }
          }
        }
      },
      "CallbackWidget": {
        "type": "object",
        "description": "Callback widget (Strapi entity).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Widget id.",
            "example": 15
          },
          "attributes": {
            "$ref": "#/components/schemas/CallbackWidgetAttributes"
          }
        }
      },
      "CallbackWidgetInput": {
        "type": "object",
        "description": "Writable callback widget fields. `sitename` and `widget_name` are required on create.",
        "properties": {
          "sitename": {
            "type": "string",
            "description": "Domain of the site where the widget is installed, without protocol and `www.` (unique across the platform).",
            "example": "example.com"
          },
          "widget_name": {
            "type": "string",
            "maxLength": 100,
            "description": "Widget name.",
            "example": "Main site"
          },
          "activity": {
            "type": "boolean",
            "description": "Enable the widget (default `false`).",
            "example": true
          },
          "redirect": {
            "$ref": "#/components/schemas/CallbackWidgetRedirect"
          },
          "work_time": {
            "type": "object",
            "description": "Working hours by weekday (`mon`..`sun`), value `HH:mm-HH:mm`.",
            "additionalProperties": {
              "type": "string",
              "description": "Time range `HH:mm-HH:mm`.",
              "example": "09:00-18:00"
            },
            "example": {
              "mon": "09:00-18:00",
              "tue": "09:00-18:00",
              "wed": "09:00-18:00",
              "thu": "09:00-18:00",
              "fri": "09:00-18:00"
            }
          },
          "active_on_exit": {
            "type": "boolean",
            "description": "Show the form on exit intent (default `false`).",
            "example": true
          },
          "hide_on_weekend": {
            "type": "boolean",
            "description": "Hide the widget on days without `work_time` (default `false`).",
            "example": false
          },
          "callback_widget_activity": {
            "type": "integer",
            "description": "Id of existing activity settings to link (optional; usually created afterwards via `POST /callback-widget-activities`).",
            "example": 29
          }
        }
      },
      "CallbackWidgetTicketAttributes": {
        "type": "object",
        "description": "Callback ticket attributes.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Ticket id (present only in flat responses).",
            "example": 101
          },
          "contact_number": {
            "type": "string",
            "minLength": 9,
            "maxLength": 15,
            "description": "Visitor phone number.",
            "example": "+380441234567"
          },
          "when_call_time": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Requested call time.",
            "example": "2026-01-20T14:10:00.000Z"
          },
          "ticket_status": {
            "type": "string",
            "enum": [
              "queued",
              "calling",
              "call_success",
              "call_success_left",
              "canceled",
              "call_unsuccess"
            ],
            "description": "Status: `queued` — waiting for the call time; `calling` — call in progress; `call_success` — visitor answered; `call_success_left` — employee answered, visitor not reached; `canceled` — call cancelled; `call_unsuccess` — employee/group not reached.",
            "example": "call_success"
          },
          "action_id": {
            "type": "string",
            "description": "Call action id generated by the server to match the ticket with call statistics.",
            "example": "1737000000"
          },
          "isFirstTime": {
            "type": "boolean",
            "description": "`true` if this number left a ticket for this widget for the first time.",
            "example": true
          },
          "domain": {
            "type": [
              "string",
              "null"
            ],
            "description": "URL of the page where the request was made.",
            "example": "https://example.com/pricing"
          },
          "ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "Visitor IP address (set by the server).",
            "example": "203.0.113.10"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at (ISO-8601, UTC).",
            "example": "2026-01-20T14:10:00.673Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at (ISO-8601, UTC).",
            "example": "2026-01-20T14:15:19.138Z"
          },
          "callback_widget": {
            "type": "object",
            "description": "Widget of the ticket (populate).",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/CallbackWidget"
              }
            }
          },
          "statistics": {
            "type": "object",
            "description": "Call legs of the callback (populate; see Calls & Statistics).",
            "properties": {
              "data": {
                "type": "array",
                "description": "Call records.",
                "items": {
                  "type": "object",
                  "description": "Call record (`{ id, attributes }`)."
                }
              }
            }
          }
        }
      },
      "CallbackWidgetTicket": {
        "type": "object",
        "description": "Callback ticket (Strapi entity).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Ticket id.",
            "example": 101
          },
          "attributes": {
            "$ref": "#/components/schemas/CallbackWidgetTicketAttributes"
          }
        }
      },
      "CallbackWidgetActivityAttributes": {
        "type": "object",
        "description": "Widget activity (behaviour) settings.",
        "properties": {
          "countdown": {
            "type": "integer",
            "maximum": 300,
            "description": "Seconds of countdown shown to the visitor before the callback (`%countdown%` in texts).",
            "example": 30
          },
          "after_open": {
            "type": "integer",
            "maximum": 300,
            "description": "Seconds after page load before the widget form opens automatically.",
            "example": 5
          },
          "limit": {
            "type": "integer",
            "description": "Maximum number of queued (not yet processed) tickets from one IP address; further requests get 400 `IP limit!`.",
            "example": 3
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "callback_widget": {
            "type": "object",
            "description": "Widget these settings belong to (populate).",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/CallbackWidget"
              }
            }
          }
        }
      },
      "CallbackWidgetActivity": {
        "type": "object",
        "description": "Widget activity settings (Strapi entity).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Settings id.",
            "example": 29
          },
          "attributes": {
            "$ref": "#/components/schemas/CallbackWidgetActivityAttributes"
          }
        }
      },
      "CallbackWidgetActivityInput": {
        "type": "object",
        "description": "Writable activity fields. On create `countdown`, `after_open`, `limit` are required.",
        "properties": {
          "countdown": {
            "type": "integer",
            "maximum": 300,
            "description": "Countdown before the callback, seconds (0–300).",
            "example": 30
          },
          "after_open": {
            "type": "integer",
            "maximum": 300,
            "description": "Delay before the form opens automatically, seconds (0–300).",
            "example": 5
          },
          "limit": {
            "type": "integer",
            "description": "Maximum queued tickets from one IP.",
            "example": 3
          },
          "callback_widget": {
            "type": "integer",
            "description": "Callback widget id to attach the settings to.",
            "example": 15
          }
        }
      },
      "CallbackWidgetDesignLocation": {
        "type": "object",
        "description": "Widget button position (component `location`).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (response only).",
            "example": 1
          },
          "corner_location": {
            "type": "string",
            "enum": [
              "left",
              "right"
            ],
            "description": "Screen corner: bottom-left or bottom-right.",
            "example": "right"
          },
          "padding_x": {
            "type": "integer",
            "description": "Horizontal offset from the edge, px (required).",
            "example": 10
          },
          "padding_y": {
            "type": "integer",
            "description": "Vertical offset from the edge, px (required).",
            "example": 10
          }
        }
      },
      "CallbackWidgetDesignInscription": {
        "type": "object",
        "description": "Set of texts of the widget form (component `inscription`). Placeholders: `%countdown%` — seconds, `%country%` — country name.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (response only).",
            "example": 1
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 70,
            "description": "Form title.",
            "example": "Have a question? We will call you back!"
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 150,
            "description": "Form text.",
            "example": "We will call you in %countdown% seconds. It is fast and free!"
          },
          "button_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20,
            "description": "Submit button text.",
            "example": "Call me"
          },
          "phone_placeholder": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "description": "Phone input placeholder.",
            "example": "Enter your phone number"
          },
          "call_later_label": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 40,
            "description": "Label of the \"call me later\" option.",
            "example": "Not a good time to talk?"
          },
          "blocked_country": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 150,
            "description": "Message when the number's country cannot be called or the number is invalid.",
            "example": "Sorry, we cannot call %country% or the number is incorrect."
          },
          "call_limit": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 150,
            "description": "Message when the visitor hit the request limit.",
            "example": "You can order another callback in half an hour."
          },
          "make_order": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Message after the request is accepted.",
            "example": "Request accepted. We are calling you now!"
          },
          "call_now": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Label of the \"call now\" option.",
            "example": "Call now"
          },
          "thanks": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 150,
            "description": "Thank-you message.",
            "example": "Thank you!"
          }
        }
      },
      "CallbackWidgetDesignAttributes": {
        "type": "object",
        "description": "Widget design attributes.",
        "properties": {
          "button": {
            "type": "string",
            "enum": [
              "default"
            ],
            "description": "Button style (only `default`).",
            "example": "default"
          },
          "button_size": {
            "type": "string",
            "enum": [
              "big",
              "medium"
            ],
            "description": "Button size.",
            "example": "medium"
          },
          "button_animation": {
            "type": "boolean",
            "description": "Animate the button.",
            "example": true
          },
          "button_color": {
            "type": "string",
            "description": "Button colour (CSS/hex).",
            "example": "#3538CD"
          },
          "block_color": {
            "type": "string",
            "description": "Form background colour (CSS/hex).",
            "example": "#F2F2F2"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "location": {
            "$ref": "#/components/schemas/CallbackWidgetDesignLocation"
          },
          "inscription": {
            "type": "array",
            "description": "Text sets of the form (populate).",
            "items": {
              "$ref": "#/components/schemas/CallbackWidgetDesignInscription"
            }
          },
          "callback_widget": {
            "type": "object",
            "description": "Widget this design belongs to (populate).",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/CallbackWidget"
              }
            }
          }
        }
      },
      "CallbackWidgetDesign": {
        "type": "object",
        "description": "Widget design (Strapi entity).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Design id.",
            "example": 29
          },
          "attributes": {
            "$ref": "#/components/schemas/CallbackWidgetDesignAttributes"
          }
        }
      },
      "CallbackWidgetDesignInput": {
        "type": "object",
        "description": "Writable design fields. On create `button_size`, `button_color`, `block_color` are required (`button` defaults to `default`).",
        "properties": {
          "callback_widget": {
            "type": "integer",
            "description": "Callback widget id to attach the design to.",
            "example": 15
          },
          "button": {
            "type": "string",
            "enum": [
              "default"
            ],
            "description": "Button style.",
            "example": "default"
          },
          "button_size": {
            "type": "string",
            "enum": [
              "big",
              "medium"
            ],
            "description": "Button size.",
            "example": "medium"
          },
          "button_animation": {
            "type": "boolean",
            "description": "Animate the button (default `false`).",
            "example": true
          },
          "button_color": {
            "type": "string",
            "description": "Button colour (CSS/hex).",
            "example": "#3538CD"
          },
          "block_color": {
            "type": "string",
            "description": "Form background colour (CSS/hex).",
            "example": "#F2F2F2"
          },
          "location": {
            "$ref": "#/components/schemas/CallbackWidgetDesignLocation"
          },
          "inscription": {
            "type": "array",
            "description": "Text sets of the form; the whole list is replaced on update.",
            "items": {
              "$ref": "#/components/schemas/CallbackWidgetDesignInscription"
            }
          }
        }
      },
      "CallbackWidgetMessenger": {
        "type": "object",
        "description": "Messenger link shown in the recall widget (component `messenger-info`).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (response only).",
            "example": 5
          },
          "display": {
            "type": "boolean",
            "description": "Show the messenger button (required). If `true`, `nickName` is required.",
            "example": true
          },
          "nickName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Messenger username / phone the button links to.",
            "example": "example_support"
          }
        }
      },
      "CallbackWidgetColorState": {
        "type": "object",
        "description": "Button colours for one state (component `color-state`). A recall widget has exactly 4 states.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (response only).",
            "example": 1
          },
          "text_color": {
            "type": "string",
            "description": "Text colour (CSS/hex), required.",
            "example": "#FFFFFF"
          },
          "background_color": {
            "type": "string",
            "description": "Background colour (CSS/hex), required.",
            "example": "#3538CD"
          }
        }
      },
      "CallbackWidgetRecallAttributes": {
        "type": "object",
        "description": "Recall (call-from-site) widget attributes.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "maxLength": 50,
            "description": "Widget name.",
            "example": "Call us"
          },
          "active": {
            "type": "boolean",
            "description": "Widget is enabled.",
            "example": true
          },
          "text": {
            "type": "string",
            "maxLength": 100,
            "description": "Button text.",
            "example": "Call us from the site"
          },
          "shape": {
            "type": "string",
            "enum": [
              "circle",
              "square"
            ],
            "description": "Button shape.",
            "example": "circle"
          },
          "font": {
            "type": "string",
            "description": "Font family of the widget.",
            "example": "Inter"
          },
          "uuid": {
            "type": "string",
            "description": "Public widget identifier used in the embed code; generated by the server, read-only.",
            "example": "0f8fad5b-d9cb-469f-a165-70867728950e"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at (ISO-8601, UTC).",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "redirect_data": {
            "$ref": "#/components/schemas/CallbackWidgetRedirect"
          },
          "telegram": {
            "$ref": "#/components/schemas/CallbackWidgetMessenger"
          },
          "viber": {
            "$ref": "#/components/schemas/CallbackWidgetMessenger"
          },
          "color_state": {
            "type": "array",
            "description": "Four colour states of the button (populate).",
            "items": {
              "$ref": "#/components/schemas/CallbackWidgetColorState"
            }
          },
          "sip": {
            "type": "object",
            "description": "Dedicated SIP line created for the widget (populate). Credentials are not returned.",
            "properties": {
              "data": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "SIP line `{ id, attributes: { number, ... } }`.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "SIP line id.",
                    "example": 12
                  },
                  "attributes": {
                    "type": "object",
                    "description": "SIP line attributes (see SIP).",
                    "properties": {
                      "number": {
                        "type": "string",
                        "description": "Internal SIP number of the widget (starts with 3).",
                        "example": "3101"
                      }
                    }
                  }
                }
              }
            }
          },
          "statistics": {
            "type": "object",
            "description": "Call statistics records of calls from the widget (populate; see Calls & Statistics).",
            "properties": {
              "data": {
                "type": "array",
                "description": "Call records.",
                "items": {
                  "type": "object",
                  "description": "Call record (`{ id, attributes }`)."
                }
              }
            }
          }
        }
      },
      "CallbackWidgetRecall": {
        "type": "object",
        "description": "Recall widget (Strapi entity).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Recall widget id.",
            "example": 4
          },
          "attributes": {
            "$ref": "#/components/schemas/CallbackWidgetRecallAttributes"
          }
        }
      },
      "CallbackWidgetRecallInput": {
        "type": "object",
        "description": "Writable recall widget fields. On create all of `name`, `redirect_data`, `telegram`, `viber`, `text`, `shape`, `font`, `color_state` (4 items) and `domain` are required.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "maxLength": 50,
            "description": "Widget name.",
            "example": "Call us"
          },
          "active": {
            "type": "boolean",
            "description": "Enable the widget (default `false`).",
            "example": true
          },
          "domain": {
            "type": "integer",
            "description": "SIP domain id of the company in which the widget SIP line is created (create only; ignored on update).",
            "example": 3
          },
          "redirect_data": {
            "$ref": "#/components/schemas/CallbackWidgetRedirect"
          },
          "telegram": {
            "$ref": "#/components/schemas/CallbackWidgetMessenger"
          },
          "viber": {
            "$ref": "#/components/schemas/CallbackWidgetMessenger"
          },
          "text": {
            "type": "string",
            "maxLength": 100,
            "description": "Button text.",
            "example": "Call us from the site"
          },
          "shape": {
            "type": "string",
            "enum": [
              "circle",
              "square"
            ],
            "description": "Button shape.",
            "example": "circle"
          },
          "font": {
            "type": "string",
            "description": "Font family.",
            "example": "Inter"
          },
          "color_state": {
            "type": "array",
            "minItems": 4,
            "maxItems": 4,
            "description": "Exactly 4 colour states of the button.",
            "items": {
              "$ref": "#/components/schemas/CallbackWidgetColorState"
            }
          }
        }
      },
      "CallsQueryBody": {
        "type": "object",
        "description": "Strapi query passed in the body.",
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "type": "object",
            "description": "Query parameters.",
            "properties": {
              "filters": {
                "type": "object",
                "description": "Strapi filters object, e.g. `{ \"callSetupTime\": { \"$gte\": 1790899200 } }`.",
                "example": {
                  "callSetupTime": {
                    "$gte": 1790899200
                  }
                }
              },
              "sort": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Sort, e.g. `[\"id:desc\"]`.",
                "example": [
                  "id:desc"
                ]
              },
              "pagination": {
                "type": "object",
                "description": "Pagination.",
                "properties": {
                  "page": {
                    "type": "integer",
                    "description": "Page number.",
                    "example": 1
                  },
                  "pageSize": {
                    "type": "integer",
                    "description": "Page size.",
                    "example": 25
                  }
                }
              },
              "populate": {
                "type": "object",
                "description": "Relations to include (`sip`, `sipTo`, `callRecord`, `contacts`, `group`, `ivr`…).",
                "example": {
                  "callRecord": true
                }
              },
              "locale": {
                "type": "string",
                "description": "Locale (unused for calls).",
                "example": "en"
              }
            }
          }
        }
      },
      "CallsRecordFile": {
        "type": "object",
        "description": "Call recording file.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "File id.",
            "example": 77
          },
          "name": {
            "type": "string",
            "description": "File name.",
            "example": "call-1001-mix.wav"
          },
          "ext": {
            "type": "string",
            "description": "Extension.",
            "example": ".wav"
          },
          "mime": {
            "type": "string",
            "description": "MIME type.",
            "example": "audio/wave"
          },
          "size": {
            "type": "number",
            "description": "Size in megabytes.",
            "example": 0.15
          },
          "url": {
            "type": "string",
            "description": "Relative URL; download from `https://api.onevoiplanet.com<url>`.",
            "example": "/uploads/recordings/call-1001-mix.wav"
          }
        }
      },
      "CallsStatisticFields": {
        "type": "object",
        "description": "Call record fields.",
        "properties": {
          "from": {
            "type": "string",
            "description": "Caller: external number without `+`, or internal SIP id like `001domainexample` for calls from an extension.",
            "example": "380661234567"
          },
          "to": {
            "type": "string",
            "description": "Callee: external number or internal SIP id.",
            "example": "380441234567"
          },
          "code": {
            "type": "string",
            "description": "Final SIP code: 200 answered, 486 busy, 487 cancelled/not answered, 404 not found/offline, 480 unavailable, 603 declined.",
            "example": "200"
          },
          "reason": {
            "type": "string",
            "description": "SIP reason phrase.",
            "example": "Ok"
          },
          "callSide": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "origination",
              "termination",
              null
            ],
            "description": "`termination` — incoming from the carrier, `origination` — outgoing to the carrier; null for internal calls.",
            "example": "termination"
          },
          "callDirection": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Legacy direction flag, usually null; use `callSide`, `sip`/`sipTo`.",
            "example": null
          },
          "callSetupTime": {
            "type": "string",
            "description": "Unix time (seconds) when the call was initiated.",
            "example": "1790931273"
          },
          "callStartTime": {
            "type": "string",
            "description": "Unix time when the call was answered (0 if not answered).",
            "example": "1790931283"
          },
          "callEndTime": {
            "type": "string",
            "description": "Unix time when the call ended.",
            "example": "1790931403"
          },
          "callDuration": {
            "type": "integer",
            "description": "Talk time in seconds.",
            "example": 120
          },
          "tarificatedDuration": {
            "type": "integer",
            "description": "Billed seconds.",
            "example": 120
          },
          "cost": {
            "type": "number",
            "description": "Call cost in the company currency.",
            "example": 0.05
          },
          "callCommentary": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free-text comment (see assignCommentary).",
            "example": null
          },
          "callEnder": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "true — the caller hung up, false — the callee.",
            "example": false
          },
          "isSystem": {
            "type": "boolean",
            "description": "System call (e.g. callback leg).",
            "example": false
          },
          "isFromGroup": {
            "type": "boolean",
            "description": "Call came through a call group.",
            "example": true
          },
          "isRecall": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Initiated by a recall widget.",
            "example": null
          },
          "origin_call_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Parent call id for transferred/forked legs.",
            "example": null
          },
          "originate_call_direction": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "leftLeg",
              "rightLeg",
              null
            ],
            "description": "Leg of an originated (callback) call.",
            "example": null
          },
          "callRecordStatus": {
            "type": "string",
            "enum": [
              "compiling",
              "ready",
              "disabled"
            ],
            "description": "Recording state: compiling (being prepared), ready (file in `callRecord`), disabled (not recorded).",
            "example": "ready"
          },
          "callDetailInfo": {
            "type": "object",
            "description": "Details of the legs (`subbranches`) for group/IVR/transfer calls.",
            "example": {
              "subbranches": []
            }
          },
          "refered_to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Target of a SIP REFER (transfer).",
            "example": null
          },
          "redirectedFromSipToOutbound": {
            "type": "boolean",
            "description": "Call was redirected from an extension to an external number.",
            "example": false
          },
          "ai_used": {
            "type": "boolean",
            "description": "AI processing was applied.",
            "example": false
          },
          "call_transcribation_data": {
            "type": [
              "object",
              "null"
            ],
            "description": "Transcription data (if any).",
            "example": null
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Record created at.",
            "example": "2026-02-01T09:30:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Record updated at.",
            "example": "2026-02-01T09:32:05.000Z"
          }
        }
      },
      "CallsStatisticItem": {
        "type": "object",
        "description": "Call (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Call id.",
            "example": 1001
          },
          "attributes": {
            "description": "Call attributes.",
            "allOf": [
              {
                "$ref": "#/components/schemas/CallsStatisticFields"
              },
              {
                "type": "object",
                "description": "Populated relations.",
                "properties": {
                  "sip": {
                    "type": "object",
                    "description": "Calling SIP line (outgoing/internal calls).",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/CallsSipRef"
                      }
                    }
                  },
                  "sipTo": {
                    "type": "object",
                    "description": "Called SIP line (incoming calls).",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/CallsSipRef"
                      }
                    }
                  },
                  "conversation_sips": {
                    "type": "object",
                    "description": "All SIP lines that took part in the conversation.",
                    "properties": {
                      "data": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/CallsSipRef"
                        },
                        "description": "SIP lines."
                      }
                    }
                  },
                  "callRecord": {
                    "type": "object",
                    "description": "Recording file (with `populate[callRecord]`).",
                    "properties": {
                      "data": {
                        "type": "object",
                        "description": "File envelope (null if none).",
                        "properties": {
                          "id": {
                            "type": "integer",
                            "description": "File id.",
                            "example": 77
                          },
                          "attributes": {
                            "$ref": "#/components/schemas/CallsRecordFile"
                          }
                        }
                      }
                    }
                  },
                  "contacts": {
                    "type": "array",
                    "description": "Address-book contacts whose number equals `from` or `to` (with `populate[contacts]`).",
                    "items": {
                      "type": "object",
                      "description": "Contact.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Contact id.",
                          "example": 9
                        },
                        "number": {
                          "type": "string",
                          "description": "Number.",
                          "example": "380661234567"
                        },
                        "name": {
                          "type": "string",
                          "description": "Name.",
                          "example": "John Doe"
                        }
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "CallsSipRef": {
        "type": "object",
        "description": "SIP line reference.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "SIP line id.",
            "example": 12
          },
          "attributes": {
            "type": "object",
            "description": "SIP line fields.",
            "properties": {
              "number": {
                "type": "string",
                "description": "Extension number.",
                "example": "001"
              }
            }
          }
        }
      },
      "CallsStatisticFlat": {
        "allOf": [
          {
            "type": "object",
            "description": "Call (flat object).",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Call id.",
                "example": 1001
              },
              "callRecord": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/CallsRecordFile"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Recording file (with `populate[callRecord]`)."
              }
            }
          },
          {
            "$ref": "#/components/schemas/CallsStatisticFields"
          }
        ]
      },
      "CallsMissedCall": {
        "type": "object",
        "description": "Missed-call entry.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Entry id.",
            "example": 501
          },
          "attributes": {
            "type": "object",
            "description": "Entry attributes.",
            "properties": {
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-02-01T09:30:10.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T09:30:10.000Z"
              },
              "statistic": {
                "type": "object",
                "description": "The missed call (with `populate[statistic]`).",
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallsStatisticItem"
                  }
                }
              },
              "user": {
                "type": "object",
                "description": "Employee who missed the call (with `populate[user]`).",
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "User envelope.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "User id.",
                        "example": 7
                      },
                      "attributes": {
                        "type": "object",
                        "description": "User fields.",
                        "properties": {
                          "firstName": {
                            "type": "string",
                            "description": "First name.",
                            "example": "John"
                          },
                          "lastName": {
                            "type": "string",
                            "description": "Last name.",
                            "example": "Doe"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "CallsBillingTable": {
        "type": "object",
        "description": "Table from the carrier billing.",
        "properties": {
          "data": {
            "type": "object",
            "description": "Envelope.",
            "properties": {
              "attributes": {
                "type": "object",
                "description": "Table.",
                "properties": {
                  "fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Column names.",
                    "example": [
                      "op_caller_number",
                      "called_number_out",
                      "duration",
                      "op_cost"
                    ]
                  },
                  "data": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {},
                      "description": "Row values in `fields` order."
                    },
                    "description": "Rows.",
                    "example": [
                      [
                        "380441234567",
                        "380661234567",
                        65,
                        0.05
                      ]
                    ]
                  }
                }
              }
            }
          },
          "meta": {
            "$ref": "#/components/schemas/Meta"
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "Address-book contact.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Contact id.",
            "example": 31
          },
          "attributes": {
            "type": "object",
            "description": "Contact attributes.",
            "properties": {
              "number": {
                "type": "string",
                "description": "Phone number in international format without `+`.",
                "example": "380441234567"
              },
              "FIO": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Display name of the contact.",
                "example": "John Doe"
              },
              "Comment": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Free-text note.",
                "example": "Example LLC, purchasing"
              },
              "allowlist": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "`true` — incoming calls from this number are blocked on the company's SIP domains.",
                "example": false
              },
              "statistics": {
                "type": "object",
                "description": "Linked calls (when `populate=statistics`); see Calls & Statistics.",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Call records.",
                    "items": {
                      "type": "object",
                      "description": "Call record (short).",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Call record id.",
                          "example": 9001
                        }
                      }
                    }
                  }
                }
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at (ISO-8601, UTC).",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-15T10:00:00.000Z"
              }
            }
          }
        }
      },
      "ContactInput": {
        "type": "object",
        "description": "Contact fields for create/update.",
        "properties": {
          "number": {
            "type": "string",
            "description": "Phone number, international format without `+`; unique within the company.",
            "example": "380441234567"
          },
          "FIO": {
            "type": "string",
            "description": "Display name.",
            "example": "John Doe"
          },
          "Comment": {
            "type": "string",
            "description": "Free-text note.",
            "example": "Example LLC, purchasing"
          },
          "allowlist": {
            "type": "boolean",
            "description": "`true` — block incoming calls from this number (default false).",
            "example": false
          }
        }
      },
      "EsimImage": {
        "type": "object",
        "description": "Image (Strapi media). Returned only when populated; `url` is relative to the API host.",
        "properties": {
          "data": {
            "type": [
              "object",
              "null"
            ],
            "description": "Media object.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Media id.",
                "example": 501
              },
              "attributes": {
                "type": "object",
                "description": "Media attributes (subset).",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Path of the image, e.g. `/uploads/Andorra_1a2b3c.jpg`.",
                    "example": "/uploads/Andorra_1a2b3c.jpg"
                  },
                  "name": {
                    "type": "string",
                    "description": "File name.",
                    "example": "Andorra.jpg"
                  },
                  "mime": {
                    "type": "string",
                    "description": "MIME type.",
                    "example": "image/jpeg"
                  },
                  "width": {
                    "type": "integer",
                    "description": "Width, px.",
                    "example": 612
                  },
                  "height": {
                    "type": "integer",
                    "description": "Height, px.",
                    "example": 388
                  },
                  "formats": {
                    "type": "object",
                    "description": "Resized variants (`thumbnail`, `small`, …), each with its own `url`.",
                    "example": {
                      "thumbnail": {
                        "url": "/uploads/thumbnail_Andorra_1a2b3c.jpg",
                        "width": 245,
                        "height": 155
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "EsimColour": {
        "type": "object",
        "description": "Three brand colours (hex without `#`) used to draw the country/region card.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id.",
            "example": 61
          },
          "firstColour": {
            "type": [
              "string",
              "null"
            ],
            "description": "Primary colour.",
            "example": "0021EA"
          },
          "secondColour": {
            "type": [
              "string",
              "null"
            ],
            "description": "Secondary colour.",
            "example": "FEDF00"
          },
          "thirdColour": {
            "type": [
              "string",
              "null"
            ],
            "description": "Third colour.",
            "example": "000000"
          }
        }
      },
      "EsimCountry": {
        "type": "object",
        "description": "eSIM catalogue country.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Country id (use in `filters[esim][id]` of `GET /esim-bundles`).",
            "example": 4
          },
          "attributes": {
            "type": "object",
            "description": "Country attributes.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Country name (English).",
                "example": "Andorra"
              },
              "iso_code": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "ISO 3166-1 alpha-2 code.",
                "example": "AD"
              },
              "userCountry": {
                "type": "boolean",
                "description": "Present and `true` only for the country detected from the caller's IP (list method only).",
                "example": true
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-10T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T10:00:00.000Z"
              },
              "image": {
                "$ref": "#/components/schemas/EsimImage"
              },
              "esimColour": {
                "$ref": "#/components/schemas/EsimColour"
              },
              "esim_regions": {
                "type": "object",
                "description": "Regions the country belongs to (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Regions.",
                    "items": {
                      "$ref": "#/components/schemas/EsimRegion"
                    }
                  }
                }
              },
              "esim_bundles": {
                "type": "object",
                "description": "Bundles of the country (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Bundles.",
                    "items": {
                      "$ref": "#/components/schemas/EsimBundle"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "EsimRegion": {
        "type": "object",
        "description": "eSIM catalogue region (group of countries).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Region id (use in `filters[esim_region][id]` of `GET /esim-bundles`).",
            "example": 4
          },
          "attributes": {
            "type": "object",
            "description": "Region attributes.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Region name (English).",
                "example": "Europe"
              },
              "isGlobal": {
                "type": "boolean",
                "description": "`true` for the worldwide region.",
                "example": false
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-10T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T10:00:00.000Z"
              },
              "image": {
                "$ref": "#/components/schemas/EsimImage"
              },
              "esimColour": {
                "$ref": "#/components/schemas/EsimColour"
              },
              "esim_countries": {
                "type": "object",
                "description": "Countries covered by the region (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Countries.",
                    "items": {
                      "$ref": "#/components/schemas/EsimCountry"
                    }
                  }
                }
              },
              "esim_bundles": {
                "type": "object",
                "description": "Bundles of the region (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Bundles.",
                    "items": {
                      "$ref": "#/components/schemas/EsimBundle"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "EsimBundleProvider": {
        "type": "object",
        "description": "Provider descriptor of a bundle (dynamic zone item). `__component` says which provider serves the bundle; the other fields are the provider's own identifiers.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id.",
            "example": 300
          },
          "__component": {
            "type": "string",
            "enum": [
              "esim-bundle-provider.esimgo",
              "esim-bundle-provider.keep-go",
              "esim-bundle-provider.yesim",
              "esim-bundle-provider.telna",
              "esim-bundle-provider.esimcard"
            ],
            "description": "Provider component.",
            "example": "esim-bundle-provider.esimgo"
          },
          "bundle_name": {
            "type": "string",
            "description": "eSIM-Go bundle name (`esimgo`).",
            "example": "esim_1GB_7D_AD_U"
          },
          "bundle_id": {
            "type": "string",
            "description": "KeepGo bundle id (`keep-go`).",
            "example": "1234"
          },
          "amount_mb": {
            "type": "string",
            "description": "KeepGo refill size, MB (`keep-go`).",
            "example": "1024"
          },
          "amount_days": {
            "type": "string",
            "description": "KeepGo refill validity, days (`keep-go`).",
            "example": "7"
          },
          "packageTemplateId": {
            "type": "string",
            "description": "Yesim package template id (`yesim`).",
            "example": "110237"
          },
          "packageId": {
            "type": "string",
            "description": "Telna / eSIMCard package id (`telna`, `esimcard`).",
            "example": "pkg_123"
          }
        }
      },
      "EsimPrice": {
        "type": "object",
        "description": "Price list of a bundle in one currency.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Price list id.",
            "example": 900
          },
          "attributes": {
            "type": "object",
            "description": "Price list attributes.",
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Free-text description.",
                "example": "1GB/7days Andorra"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-10T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T10:00:00.000Z"
              },
              "currency": {
                "type": "object",
                "description": "Currency of the price list (with `populate[prices][populate]=*`).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Currency.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Currency id.",
                        "example": 1
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Currency attributes.",
                        "properties": {
                          "name_short": {
                            "type": "string",
                            "description": "ISO code.",
                            "example": "USD"
                          },
                          "name_long": {
                            "type": "string",
                            "description": "Name.",
                            "example": "US Dollar"
                          },
                          "symbol": {
                            "type": "string",
                            "description": "Symbol.",
                            "example": "$"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "price_by_field": {
                "type": "array",
                "description": "Named prices; the retail price of the bundle is the item named `price`.",
                "items": {
                  "type": "object",
                  "description": "Named price.",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "description": "Item id.",
                      "example": 9001
                    },
                    "name": {
                      "type": "string",
                      "description": "Price name (`price` for bundles).",
                      "example": "price"
                    },
                    "price": {
                      "type": "number",
                      "description": "Price in the currency of the price list.",
                      "example": 4.99
                    },
                    "isPriceStatic": {
                      "type": "boolean",
                      "description": "`true` — fixed price, not recalculated from the base currency.",
                      "example": false
                    }
                  }
                }
              }
            }
          }
        }
      },
      "EsimBundle": {
        "type": "object",
        "description": "eSIM data bundle of the catalogue.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Bundle id — pass it as `esim.id` when creating an order.",
            "example": 120
          },
          "attributes": {
            "type": "object",
            "description": "Bundle attributes.",
            "properties": {
              "days_amount": {
                "type": "integer",
                "description": "Validity, days.",
                "example": 7
              },
              "usage_amount": {
                "type": "integer",
                "description": "Data volume, GB (for unlimited bundles see `isInfinity`).",
                "example": 1
              },
              "isInfinity": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "`true` — unlimited data.",
                "example": false
              },
              "isFullEsim": {
                "type": "boolean",
                "description": "`true` — bundle with minutes/SMS (full eSIM), `false` — data only.",
                "example": false
              },
              "minutes_amount": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Included minutes for full eSIMs; `-1`/`null` — not applicable.",
                "example": null
              },
              "messages_amount": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Included SMS for full eSIMs; `-1`/`null` — not applicable.",
                "example": null
              },
              "providerName": {
                "type": "string",
                "description": "Provider serving the bundle: `esimgo`, `keep-go`, `yesim`, `telna`, `esimcard`.",
                "example": "esimgo"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-10T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T10:00:00.000Z"
              },
              "esim": {
                "type": "object",
                "description": "Country of the bundle (with populate; `data: null` for regional bundles).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Country.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Country id.",
                        "example": 4
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Country attributes (see EsimCountry).",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "Country name.",
                            "example": "Andorra"
                          },
                          "iso_code": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "ISO alpha-2.",
                            "example": "AD"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Created at.",
                            "example": "2026-01-10T10:00:00.000Z"
                          },
                          "updatedAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Updated at.",
                            "example": "2026-02-01T10:00:00.000Z"
                          },
                          "image": {
                            "$ref": "#/components/schemas/EsimImage"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "esim_region": {
                "type": "object",
                "description": "Region of the bundle (with populate; `data: null` for country bundles).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Region.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Region id.",
                        "example": 4
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Region attributes (see EsimRegion).",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "Region name.",
                            "example": "Europe"
                          },
                          "isGlobal": {
                            "type": "boolean",
                            "description": "Worldwide region.",
                            "example": false
                          }
                        }
                      }
                    }
                  }
                }
              },
              "provider": {
                "type": "array",
                "description": "Provider descriptor (with populate); always one item.",
                "items": {
                  "$ref": "#/components/schemas/EsimBundleProvider"
                }
              },
              "prices": {
                "type": "object",
                "description": "Price lists, one per currency (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Price lists.",
                    "items": {
                      "$ref": "#/components/schemas/EsimPrice"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "EsimPacketUsage": {
        "type": "object",
        "description": "Bundle (packet) attached to an eSIM with live usage from the provider. Plain object (no `attributes` envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Attachment id.",
            "example": 31
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider-side bundle identifier (eSIM-Go bundle name, KeepGo bundle id, Yesim template id, …).",
            "example": "esim_1GB_7D_AD_U"
          },
          "esim_packet": {
            "type": [
              "object",
              "null"
            ],
            "description": "Catalogue bundle (flat object with the EsimBundle attributes: `id`, `days_amount`, `usage_amount`, `isInfinity`, `isFullEsim`, `providerName`, …).",
            "example": {
              "id": 120,
              "days_amount": 7,
              "usage_amount": 1,
              "isInfinity": false,
              "isFullEsim": false,
              "providerName": "esimgo"
            }
          },
          "allowed_usage_mb": {
            "type": "number",
            "description": "Total data of the bundle, MB (if the provider gave no data: nominal volume × 1024).",
            "example": 1000
          },
          "remaining_usage_mb": {
            "type": "number",
            "description": "Remaining data, MB (0 if the provider gave no data).",
            "example": 512.5
          },
          "endTime": {
            "type": [
              "string",
              "number",
              "null"
            ],
            "description": "Bundle expiry as reported by the provider: ISO-8601 string or Unix timestamp (seconds); `null` — not activated yet.",
            "example": "2026-03-17T09:00:00.000Z"
          },
          "startTime": {
            "type": [
              "string",
              "number",
              "null"
            ],
            "description": "Activation time (only some providers).",
            "example": "2026-03-10T09:00:00.000Z"
          },
          "remaining_days": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Days left until expiry (rounded up, 0 if expired); `null` — not activated.",
            "example": 5
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Bundle status from the provider (`Active`, `Not Active`, `Finished`, `Expired` for KeepGo; `active`, `queued`, `processing`, `expired`, `depleted` for eSIM-Go; others derive `Active`/`Expired` from `endTime`); `null` — unknown / not activated.",
            "example": "active"
          }
        }
      },
      "EsimCountryNetworks": {
        "type": "object",
        "description": "Mobile networks available in one country.",
        "properties": {
          "iso_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Country ISO alpha-2.",
            "example": "AD"
          },
          "mobileNetworks": {
            "type": "array",
            "description": "Operator names (strings). For `esimcard` bundles — objects `{ network_name, two_g, three_g, four_G, five_G }`.",
            "items": {
              "type": [
                "string",
                "object"
              ],
              "description": "Operator name or network object.",
              "example": "Andorra Telecom"
            }
          }
        }
      },
      "EsimMyEsim": {
        "type": "object",
        "description": "eSIM of the current user with live usage.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "eSIM record id.",
            "example": 15
          },
          "attributes": {
            "type": "object",
            "description": "eSIM data.",
            "properties": {
              "iccid": {
                "type": "string",
                "description": "ICCID of the eSIM.",
                "example": "8944538532000012345"
              },
              "lpa_code": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Activation code (`LPA:1$<SM-DP+ address>$<matching id>`) to render as QR or enter manually.",
                "example": "LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL"
              },
              "appleInstallUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "One-tap eSIM install link for iOS 17.4+.",
                "example": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL"
              },
              "packets": {
                "type": "array",
                "description": "Bundles on the eSIM, newest first, with live usage.",
                "items": {
                  "$ref": "#/components/schemas/EsimPacketUsage"
                }
              },
              "mobileOperatorsInfo": {
                "type": "array",
                "description": "Networks per country for the latest bundle (empty on provider error).",
                "items": {
                  "$ref": "#/components/schemas/EsimCountryNetworks"
                }
              },
              "first_packet": {
                "type": "object",
                "description": "Bundle the eSIM was bought with, including its country/region (with images).",
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/EsimBundle"
                  }
                }
              }
            }
          }
        }
      },
      "EsimRecord": {
        "type": "object",
        "description": "Stored eSIM record (no live usage).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "eSIM record id.",
            "example": 15
          },
          "attributes": {
            "type": "object",
            "description": "Record attributes.",
            "properties": {
              "iccid": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "ICCID.",
                "example": "8944538532000012345"
              },
              "provider": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Provider: `esimgo`, `keep-go`, `yesim`, `telna`, `esimcard`.",
                "example": "esimgo"
              },
              "lpa_code": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Activation code.",
                "example": "LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL"
              },
              "appleInstallUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "One-tap install link for iOS.",
                "example": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at (purchase time).",
                "example": "2026-03-10T09:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-03-10T09:00:00.000Z"
              },
              "esim_packets": {
                "type": "array",
                "description": "Attached bundles (with populate): `templateId` and the catalogue bundle `esim_packet`.",
                "items": {
                  "type": "object",
                  "description": "Attachment.",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "description": "Attachment id.",
                      "example": 31
                    },
                    "templateId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Provider-side bundle identifier.",
                      "example": "esim_1GB_7D_AD_U"
                    },
                    "esim_packet": {
                      "type": "object",
                      "description": "Catalogue bundle.",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/EsimBundle"
                        }
                      }
                    }
                  }
                }
              },
              "first_packet": {
                "type": "object",
                "description": "First bundle (with populate).",
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/EsimBundle"
                  }
                }
              },
              "orders": {
                "type": "object",
                "description": "Orders that bought/topped up the eSIM (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Orders (see Orders).",
                    "items": {
                      "type": "object",
                      "description": "Order reference.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Order id.",
                          "example": 77
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Order attributes (see Orders).",
                          "example": {
                            "orderId": "1710061200000123"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "IntegrationsFields": {
        "type": "object",
        "description": "Credentials of your CRM. Which fields apply depends on `serviceName`: keycrm — `webhookUrl`; salesdrive — `crmBaseUrl`, `crmApiKey`; nethunt — `email`, `apiKey`; pipedrive — `api_token`; hubspot — `api_token` (private app token); uspacy — `domain`, `email`, `jwt`, `refresh_token`; onebox — `crmBaseUrl`, `login`, `restapipassword` (then `POST /onebox/generate-token`); zohocrm — via `POST /zohocrm/generate-tokens`. `active` applies to all.",
        "properties": {
          "active": {
            "type": "boolean",
            "default": true,
            "description": "Push call events to this CRM.",
            "example": true
          },
          "webhookUrl": {
            "type": "string",
            "description": "keycrm: telephony webhook URL from KeyCRM settings.",
            "example": "https://openapi.keycrm.app/v1/communication/call-route/********"
          },
          "crmBaseUrl": {
            "type": "string",
            "description": "salesdrive / onebox: base URL of your CRM instance.",
            "example": "https://example.salesdrive.me"
          },
          "crmApiKey": {
            "type": "string",
            "description": "salesdrive: API key.",
            "example": "********"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "nethunt / uspacy: account email.",
            "example": "owner@example.com"
          },
          "apiKey": {
            "type": "string",
            "description": "nethunt: API key.",
            "example": "********"
          },
          "api_token": {
            "type": "string",
            "description": "pipedrive / hubspot: API token.",
            "example": "********"
          },
          "domain": {
            "type": "string",
            "description": "uspacy: workspace domain.",
            "example": "example.uspacy.ua"
          },
          "jwt": {
            "type": "string",
            "description": "uspacy: access JWT.",
            "example": "********"
          },
          "refresh_token": {
            "type": "string",
            "description": "uspacy / zohocrm: refresh token.",
            "example": "********"
          },
          "login": {
            "type": "string",
            "description": "onebox: user login.",
            "example": "owner@example.com"
          },
          "restapipassword": {
            "type": "string",
            "description": "onebox: REST API password.",
            "example": "********"
          }
        }
      },
      "IntegrationsRecord": {
        "type": "object",
        "description": "Stored integration record (fields depend on the CRM type; secrets are returned as stored).",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "integer",
            "description": "Integration record id (use in edit/delete).",
            "example": 5
          },
          "active": {
            "type": "boolean",
            "description": "Push enabled.",
            "example": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-10-02T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-10-02T10:00:00.000Z"
          }
        }
      },
      "IntegrationsComponent": {
        "type": "object",
        "description": "Integration item of the company. The record is under the key equal to `serviceName`.",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (not used in API calls).",
            "example": 2
          },
          "__component": {
            "type": "string",
            "description": "`integration.<serviceName>`.",
            "example": "integration.keycrm"
          },
          "serviceName": {
            "type": "string",
            "enum": [
              "keycrm",
              "salesdrive",
              "nethunt",
              "zohocrm",
              "pipedrive",
              "onebox",
              "hubspot",
              "uspacy"
            ],
            "description": "CRM type.",
            "example": "keycrm"
          },
          "keycrm": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          },
          "salesdrive": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          },
          "nethunt": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          },
          "zohocrm": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          },
          "pipedrive": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          },
          "onebox": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          },
          "hubspot": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          },
          "uspacy": {
            "$ref": "#/components/schemas/IntegrationsRecord"
          }
        }
      },
      "IntegrationsCompanyResult": {
        "type": "object",
        "description": "Company (flat object) with its integrations; other company fields are omitted here.",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "integer",
            "description": "Company id.",
            "example": 7
          },
          "Name": {
            "type": "string",
            "description": "Company name.",
            "example": "Example LLC"
          },
          "integration": {
            "type": "array",
            "description": "Connected integrations.",
            "items": {
              "$ref": "#/components/schemas/IntegrationsComponent"
            }
          }
        }
      },
      "IvrAudioRef": {
        "type": "object",
        "description": "Company audio file relation (`data` is null when not set).",
        "properties": {
          "data": {
            "type": [
              "object",
              "null"
            ],
            "description": "Audio file or null.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Audio file id.",
                "example": 101
              },
              "attributes": {
                "type": "object",
                "description": "Audio attributes.",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Display name.",
                    "example": "Greeting"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "sound",
                      "moh"
                    ],
                    "description": "`sound` — announcement, `moh` — music on hold.",
                    "example": "sound"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "base",
                      "additional"
                    ],
                    "description": "`base` — system audio, `additional` — uploaded by the company.",
                    "example": "additional"
                  },
                  "url": {
                    "type": "string",
                    "description": "Relative URL of the file.",
                    "example": "/uploads/sound/company_1/greeting.wav"
                  },
                  "size": {
                    "type": "number",
                    "description": "File size, KB.",
                    "example": 120.5
                  }
                }
              }
            }
          }
        }
      },
      "IvrAudioListRef": {
        "type": "object",
        "description": "List of company audio files.",
        "properties": {
          "data": {
            "type": "array",
            "description": "Audio files.",
            "items": {
              "type": "object",
              "description": "Audio file.",
              "properties": {
                "id": {
                  "type": "integer",
                  "description": "Audio file id.",
                  "example": 101
                },
                "attributes": {
                  "type": "object",
                  "description": "Audio attributes (see IvrAudioRef).",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Display name.",
                      "example": "Greeting"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "sound",
                        "moh"
                      ],
                      "description": "Audio type.",
                      "example": "sound"
                    },
                    "category": {
                      "type": "string",
                      "enum": [
                        "base",
                        "additional"
                      ],
                      "description": "`base` — system audio, `additional` — uploaded by the company.",
                      "example": "additional"
                    },
                    "url": {
                      "type": "string",
                      "description": "Relative URL of the file.",
                      "example": "/uploads/sound/company_1/greeting.wav"
                    },
                    "size": {
                      "type": "number",
                      "description": "File size, KB.",
                      "example": 120.5
                    }
                  }
                }
              }
            }
          }
        }
      },
      "IvrDomainRef": {
        "type": "object",
        "description": "SIP domain relation.",
        "properties": {
          "data": {
            "type": [
              "object",
              "null"
            ],
            "description": "Domain or null.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Domain id.",
                "example": 12
              },
              "attributes": {
                "type": "object",
                "description": "Domain attributes.",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Domain name (sub-domain of sip.onevoiplanet.com).",
                    "example": "examplellc"
                  }
                }
              }
            }
          }
        }
      },
      "IvrGroupRef": {
        "type": "object",
        "description": "Group relation.",
        "properties": {
          "data": {
            "type": [
              "object",
              "null"
            ],
            "description": "Group or null.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Group id.",
                "example": 5
              },
              "attributes": {
                "type": "object",
                "description": "Group attributes (short).",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Group name.",
                    "example": "Sales"
                  },
                  "number": {
                    "type": "string",
                    "description": "Internal 3-digit queue number.",
                    "example": "001"
                  },
                  "algoritm": {
                    "type": "string",
                    "enum": [
                      "consistent",
                      "random",
                      "uniform",
                      "to_all",
                      "less_speak_time",
                      "less_answered"
                    ],
                    "description": "Ring strategy of the group (see Group.algoritm).",
                    "example": "uniform"
                  }
                }
              }
            }
          }
        }
      },
      "IvrUserRef": {
        "type": "object",
        "description": "Employee (user) relation.",
        "properties": {
          "data": {
            "type": [
              "object",
              "null"
            ],
            "description": "Employee or null.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "User id.",
                "example": 44
              },
              "attributes": {
                "type": "object",
                "description": "User attributes (short).",
                "properties": {
                  "username": {
                    "type": "string",
                    "description": "Login.",
                    "example": "john.doe"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email.",
                    "example": "john.doe@example.com"
                  },
                  "firstName": {
                    "type": "string",
                    "description": "First name.",
                    "example": "John"
                  },
                  "lastName": {
                    "type": "string",
                    "description": "Last name.",
                    "example": "Doe"
                  }
                }
              }
            }
          }
        }
      },
      "IvrRedirect": {
        "type": "object",
        "description": "Redirect action: where the call goes. Exactly one of `user`, `group`, `call_end` is meaningful.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (internal).",
            "example": 51
          },
          "extension": {
            "type": [
              "string",
              "null"
            ],
            "description": "Digit the caller presses (for menu items; null for the IVR-level final redirect).",
            "example": "1"
          },
          "call_end": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true` — hang up; `null` when the redirect targets a user or a group.",
            "example": false
          },
          "user": {
            "$ref": "#/components/schemas/IvrUserRef"
          },
          "group": {
            "$ref": "#/components/schemas/IvrGroupRef"
          }
        }
      },
      "IvrPlayback": {
        "type": "object",
        "description": "Playback action: play an audio file and open the sub-menu (`childs`).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (internal).",
            "example": 52
          },
          "extension": {
            "type": "string",
            "description": "Digit the caller presses.",
            "example": "2"
          },
          "playback_audio": {
            "$ref": "#/components/schemas/IvrAudioRef"
          }
        }
      },
      "IvrRedirectInput": {
        "type": "object",
        "description": "Final redirect of the IVR (after `max_input` wrong inputs or `hold_time` silence). Pass ids of the target.",
        "properties": {
          "user": {
            "type": "integer",
            "description": "Employee (user) id to dial.",
            "example": 44
          },
          "group": {
            "type": "integer",
            "description": "Group id to dial.",
            "example": 5
          },
          "call_end": {
            "type": "boolean",
            "description": "`true` — hang up.",
            "example": false
          }
        }
      },
      "Ivr": {
        "type": "object",
        "description": "IVR menu.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "IVR id.",
            "example": 7
          },
          "attributes": {
            "type": "object",
            "description": "IVR attributes. Relations appear only when populated.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Name (3–50 chars).",
                "example": "Main menu"
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Free-text description (up to 250 chars).",
                "example": "Greeting and department selection"
              },
              "number": {
                "type": "string",
                "description": "Internal 3-digit IVR number inside the domain (generated).",
                "example": "001"
              },
              "hold_time": {
                "type": "integer",
                "description": "Seconds to wait for a digit before the final redirect (1–300, default 5).",
                "example": 5
              },
              "max_input": {
                "type": "integer",
                "description": "Allowed wrong inputs before the final redirect (1–10, default 3).",
                "example": 3
              },
              "redirection": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "employee",
                  "call_end",
                  "group"
                ],
                "description": "Type of the final redirect (informational; the actual target is `redirection_data`).",
                "example": "group"
              },
              "redirection_data": {
                "$ref": "#/components/schemas/IvrRedirect"
              },
              "domain": {
                "$ref": "#/components/schemas/IvrDomainRef"
              },
              "hold_time_expired_audio": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "max_input_exided_audio": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "wrong_input_audio": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "adiitional_input_audio": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "ivr_work_times": {
                "type": "object",
                "description": "Working-time blocks of the IVR (when populated).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Blocks.",
                    "items": {
                      "$ref": "#/components/schemas/IvrWorkTime"
                    }
                  }
                }
              },
              "in_phone_lines": {
                "type": "object",
                "description": "Phone numbers routed to this IVR (when populated).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Numbers (see Numbers tag).",
                    "items": {
                      "type": "object",
                      "description": "Phone number (short).",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Phone id.",
                          "example": 15
                        }
                      }
                    }
                  }
                }
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at (ISO-8601, UTC).",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-16T09:30:00.000Z"
              }
            }
          }
        }
      },
      "IvrInput": {
        "type": "object",
        "description": "IVR fields for create/update. Relations are passed as ids.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "maxLength": 50,
            "description": "Name (required on create).",
            "example": "Main menu"
          },
          "description": {
            "type": "string",
            "maxLength": 250,
            "description": "Description.",
            "example": "Greeting and department selection"
          },
          "domain": {
            "type": "integer",
            "description": "SIP domain id of the company (required on create; cannot be changed later).",
            "example": 12
          },
          "hold_time": {
            "type": "integer",
            "minimum": 1,
            "maximum": 300,
            "description": "Seconds to wait for a digit (default 5).",
            "example": 5
          },
          "max_input": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "description": "Allowed wrong inputs (default 3).",
            "example": 3
          },
          "redirection": {
            "type": "string",
            "enum": [
              "employee",
              "call_end",
              "group"
            ],
            "description": "Type of the final redirect (informational).",
            "example": "group"
          },
          "redirection_data": {
            "$ref": "#/components/schemas/IvrRedirectInput"
          },
          "hold_time_expired_audio": {
            "type": "integer",
            "description": "Audio id played when `hold_time` expires.",
            "example": 101
          },
          "max_input_exided_audio": {
            "type": "integer",
            "description": "Audio id played when `max_input` is exceeded.",
            "example": 102
          },
          "wrong_input_audio": {
            "type": "integer",
            "description": "Audio id played on a wrong digit.",
            "example": 102
          },
          "adiitional_input_audio": {
            "type": "integer",
            "description": "Additional prompt audio id (reserved).",
            "example": 104
          }
        }
      },
      "IvrWorkTimeDate": {
        "type": "object",
        "description": "Schedule: weekday → time range `HH:MM-HH:MM` (Europe/Kyiv). Only listed days are active.",
        "properties": {
          "mon": {
            "type": "string",
            "description": "Monday range.",
            "example": "09:00-18:00"
          },
          "tue": {
            "type": "string",
            "description": "Tuesday range.",
            "example": "09:00-18:00"
          },
          "wed": {
            "type": "string",
            "description": "Wednesday range.",
            "example": "09:00-18:00"
          },
          "thu": {
            "type": "string",
            "description": "Thursday range.",
            "example": "09:00-18:00"
          },
          "fri": {
            "type": "string",
            "description": "Friday range.",
            "example": "09:00-18:00"
          },
          "sat": {
            "type": "string",
            "description": "Saturday range.",
            "example": "10:00-16:00"
          },
          "sun": {
            "type": "string",
            "description": "Sunday range.",
            "example": "10:00-16:00"
          }
        }
      },
      "IvrWorkTime": {
        "type": "object",
        "description": "IVR working-time block.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Block id.",
            "example": 21
          },
          "attributes": {
            "type": "object",
            "description": "Block attributes. Relations appear only when populated.",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "any",
                  "byDates"
                ],
                "description": "`byDates` — active by schedule `date`; `any` — fallback outside all schedules.",
                "example": "byDates"
              },
              "date": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/IvrWorkTimeDate"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Schedule (null for `any`)."
              },
              "use_global_redirect": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Use the IVR-level final redirect (`redirection_data`) for this block (default true).",
                "example": true
              },
              "hello_audios": {
                "$ref": "#/components/schemas/IvrAudioListRef"
              },
              "ivr_menu_description": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "background_audio": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "work_time": {
                "type": "object",
                "description": "Parent IVR (when populated).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "IVR or null.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "IVR id.",
                        "example": 7
                      },
                      "attributes": {
                        "type": "object",
                        "description": "IVR attributes (short).",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "IVR name.",
                            "example": "Main menu"
                          },
                          "number": {
                            "type": "string",
                            "description": "IVR number.",
                            "example": "001"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "ivr_menu_items": {
                "type": "object",
                "description": "Menu items of the block (when populated).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Menu items.",
                    "items": {
                      "$ref": "#/components/schemas/IvrMenuItem"
                    }
                  }
                }
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:05:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-15T10:05:00.000Z"
              }
            }
          }
        }
      },
      "IvrWorkTimeInput": {
        "type": "object",
        "description": "Working-time block fields. Relations are passed as ids.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "any",
              "byDates"
            ],
            "description": "Block type (required on create).",
            "example": "byDates"
          },
          "date": {
            "$ref": "#/components/schemas/IvrWorkTimeDate"
          },
          "work_time": {
            "type": "integer",
            "description": "Parent IVR id.",
            "example": 7
          },
          "hello_audios": {
            "type": "array",
            "description": "Greeting audio ids, played in order before the menu.",
            "items": {
              "type": "integer"
            },
            "example": [
              101
            ]
          },
          "ivr_menu_description": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Audio id that announces the menu options. If null, the block has no menu (on update all its menu items are deleted).",
            "example": 103
          },
          "background_audio": {
            "type": "integer",
            "description": "Background audio id (reserved).",
            "example": 104
          },
          "use_global_redirect": {
            "type": "boolean",
            "description": "Use the IVR-level final redirect for this block (default true).",
            "example": true
          }
        }
      },
      "IvrMenuItem": {
        "type": "object",
        "description": "IVR menu item (one digit of a menu).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Menu item id.",
            "example": 301
          },
          "attributes": {
            "type": "object",
            "description": "Item attributes. `playback` and `redirect` are mutually exclusive (the unused one is null).",
            "properties": {
              "uuid": {
                "type": "string",
                "description": "Client-side id sent on creation.",
                "example": "a1"
              },
              "playback": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/IvrPlayback"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Playback action or null."
              },
              "redirect": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/IvrRedirect"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Redirect action or null."
              },
              "parent": {
                "type": "object",
                "description": "Parent item (when populated); `data: null` for top-level items.",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Parent or null.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Parent item id.",
                        "example": 302
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Parent attributes (short).",
                        "properties": {
                          "uuid": {
                            "type": "string",
                            "description": "Parent uuid.",
                            "example": "a2"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "childs": {
                "type": "object",
                "description": "Sub-menu items (when populated).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Children.",
                    "items": {
                      "type": "object",
                      "description": "Child item (short).",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Child id.",
                          "example": 303
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Child attributes (short).",
                          "properties": {
                            "uuid": {
                              "type": "string",
                              "description": "Child uuid.",
                              "example": "a2-1"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "ivr_work_time": {
                "type": "object",
                "description": "Working-time block the item belongs to (when populated).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Block or null.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Block id.",
                        "example": 21
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Block attributes (short).",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "any",
                              "byDates"
                            ],
                            "description": "Block type.",
                            "example": "byDates"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:10:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-15T10:10:00.000Z"
              }
            }
          }
        }
      },
      "IvrMenuItemInput": {
        "type": "object",
        "description": "One menu item in the bulk save request.",
        "required": [
          "id",
          "ivr_work_time",
          "position",
          "levels"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Your own unique string id of the item (stored as `uuid`; referenced by `parent` of child items).",
            "example": "a2"
          },
          "ivr_work_time": {
            "type": "integer",
            "description": "Working-time block id (same for all items in the request).",
            "example": 21
          },
          "parent": {
            "type": [
              "string",
              "null"
            ],
            "description": "`id` of the parent item, or null for the top level.",
            "example": null
          },
          "position": {
            "type": "integer",
            "description": "Digit the caller presses (0–9); stored as `extension`.",
            "example": 2
          },
          "levels": {
            "type": "array",
            "description": "Path of digits from the root to this item; its length is the nesting depth checked against the tariff limit.",
            "items": {
              "type": "integer"
            },
            "example": [
              2
            ]
          },
          "playback": {
            "type": [
              "object",
              "null"
            ],
            "description": "Play audio and open the sub-menu. Null if this item is a redirect.",
            "properties": {
              "playback_audio": {
                "type": "object",
                "description": "Audio to play.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "Company audio id.",
                    "example": 105
                  }
                }
              }
            }
          },
          "redirect": {
            "type": "object",
            "description": "Redirect target. Pass `{}` when `playback` is used. Exactly one of `user`, `group`, `call_end`.",
            "properties": {
              "user": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Employee to dial.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "User id.",
                    "example": 44
                  }
                }
              },
              "group": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Group to dial.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "Group id.",
                    "example": 5
                  }
                }
              },
              "call_end": {
                "type": "boolean",
                "description": "`true` — hang up.",
                "example": false
              }
            }
          },
          "childs": {
            "type": "array",
            "description": "Ignored by the server (nesting is taken from `parent`); may be sent as `[]`.",
            "items": {
              "type": "object"
            },
            "example": []
          }
        }
      },
      "Group": {
        "type": "object",
        "description": "Call group (Asterisk queue).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Group id.",
            "example": 5
          },
          "attributes": {
            "type": "object",
            "description": "Group attributes. Relations appear only when populated.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Name (unique within the domain).",
                "example": "Sales"
              },
              "number": {
                "type": "string",
                "description": "Internal 3-digit queue number inside the domain.",
                "example": "001"
              },
              "algoritm": {
                "type": "string",
                "enum": [
                  "consistent",
                  "random",
                  "uniform",
                  "to_all",
                  "less_speak_time",
                  "less_answered"
                ],
                "description": "Ring strategy: `consistent` — in penalty order (linear), `random`, `uniform` — round-robin, `to_all` — ring all, `less_speak_time` — fewest calls, `less_answered` — least recent.",
                "example": "uniform"
              },
              "timeout": {
                "type": "integer",
                "description": "Seconds to ring one member before moving on (default 90).",
                "example": 60
              },
              "retry": {
                "type": "integer",
                "description": "Pause in seconds before the next ring attempt (5–360, default 5).",
                "example": 5
              },
              "wrapuptime": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Seconds a member is not called after finishing a call.",
                "example": 5
              },
              "maxlen": {
                "type": "integer",
                "description": "Max callers waiting in the queue (0 = unlimited).",
                "example": 0
              },
              "servicelevel": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Service-level threshold in seconds for statistics.",
                "example": 30
              },
              "anounceHoldTime": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "yes",
                  "no",
                  "once"
                ],
                "description": "Announce estimated hold time to waiting callers.",
                "example": "no"
              },
              "queue_number": {
                "type": "boolean",
                "description": "Announce the caller's position in the queue.",
                "example": false
              },
              "redirect": {
                "type": "boolean",
                "description": "Redirect flag (reserved).",
                "example": false
              },
              "to_anwer_remain": {
                "type": "boolean",
                "description": "Reserved flag.",
                "example": false
              },
              "domain": {
                "$ref": "#/components/schemas/IvrDomainRef"
              },
              "group_employees": {
                "type": "object",
                "description": "Memberships (when populated).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Memberships.",
                    "items": {
                      "$ref": "#/components/schemas/GroupEmployee"
                    }
                  }
                }
              },
              "audio_musiconhold": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "company_audio": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_youarenext": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_thereare": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_callswaiting": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_holdtime": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_minute": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_seconds": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_lessthan": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_thankyou": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "audio_reporthold": {
                "$ref": "#/components/schemas/IvrAudioRef"
              },
              "employees_count": {
                "type": "integer",
                "description": "Number of members (search-index mode only).",
                "example": 1
              },
              "inner_number": {
                "type": "string",
                "description": "Internal SIP address of the queue (search-index mode only).",
                "example": "2001@examplellc.sip.onevoiplanet.com"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-15T10:00:00.000Z"
              }
            }
          }
        }
      },
      "GroupInput": {
        "type": "object",
        "description": "Group fields for create/update. Audio relations are company audio ids (`audio_musiconhold` must be of type `moh`).",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name, unique within the domain (required on create).",
            "example": "Sales"
          },
          "domain": {
            "type": "integer",
            "description": "SIP domain id (required on create; cannot be changed).",
            "example": 12
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{3}$",
            "description": "3-digit queue number; generated if omitted.",
            "example": "001"
          },
          "algoritm": {
            "type": "string",
            "enum": [
              "consistent",
              "random",
              "uniform",
              "to_all",
              "less_speak_time",
              "less_answered"
            ],
            "description": "Ring strategy (required on create).",
            "example": "uniform"
          },
          "timeout": {
            "type": "integer",
            "description": "Seconds to ring one member (default 90).",
            "example": 60
          },
          "retry": {
            "type": "integer",
            "minimum": 5,
            "maximum": 360,
            "description": "Pause before the next attempt, seconds (default 5).",
            "example": 5
          },
          "wrapuptime": {
            "type": "integer",
            "description": "Wrap-up time, seconds.",
            "example": 5
          },
          "maxlen": {
            "type": "integer",
            "description": "Max waiting callers (0 = unlimited).",
            "example": 0
          },
          "servicelevel": {
            "type": "integer",
            "description": "Service-level threshold, seconds.",
            "example": 30
          },
          "anounceHoldTime": {
            "type": "string",
            "enum": [
              "yes",
              "no",
              "once"
            ],
            "description": "Announce hold time.",
            "example": "once"
          },
          "queue_number": {
            "type": "boolean",
            "description": "Announce queue position.",
            "example": true
          },
          "redirect": {
            "type": "boolean",
            "description": "Reserved flag.",
            "example": false
          },
          "to_anwer_remain": {
            "type": "boolean",
            "description": "Reserved flag.",
            "example": false
          },
          "audio_musiconhold": {
            "type": "integer",
            "description": "Music-on-hold audio id (type `moh`).",
            "example": 110
          },
          "company_audio": {
            "type": "integer",
            "description": "Announcement audio id.",
            "example": 111
          },
          "audio_youarenext": {
            "type": "integer",
            "description": "Audio id: \"you are next\".",
            "example": 112
          },
          "audio_thereare": {
            "type": "integer",
            "description": "Audio id: \"there are\".",
            "example": 113
          },
          "audio_callswaiting": {
            "type": "integer",
            "description": "Audio id: \"calls waiting\".",
            "example": 114
          },
          "audio_holdtime": {
            "type": "integer",
            "description": "Audio id: \"hold time\".",
            "example": 115
          },
          "audio_minute": {
            "type": "integer",
            "description": "Audio id: \"minute(s)\".",
            "example": 116
          },
          "audio_seconds": {
            "type": "integer",
            "description": "Audio id: \"seconds\".",
            "example": 117
          },
          "audio_lessthan": {
            "type": "integer",
            "description": "Audio id: \"less than\".",
            "example": 118
          },
          "audio_thankyou": {
            "type": "integer",
            "description": "Audio id: \"thank you\".",
            "example": 119
          },
          "audio_reporthold": {
            "type": "integer",
            "description": "Audio id: hold-time report.",
            "example": 120
          }
        }
      },
      "GroupEmployee": {
        "type": "object",
        "description": "Membership of an employee in a group.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Membership id.",
            "example": 71
          },
          "attributes": {
            "type": "object",
            "description": "Membership attributes. Relations appear only when populated.",
            "properties": {
              "penalty": {
                "type": "integer",
                "description": "Priority in the queue (1 = highest).",
                "example": 1
              },
              "group": {
                "$ref": "#/components/schemas/IvrGroupRef"
              },
              "user": {
                "$ref": "#/components/schemas/IvrUserRef"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:20:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-15T10:20:00.000Z"
              }
            }
          }
        }
      },
      "GroupEmployeeInput": {
        "type": "object",
        "description": "Membership fields.",
        "required": [
          "group",
          "user"
        ],
        "properties": {
          "group": {
            "type": "integer",
            "description": "Group id (must belong to the company).",
            "example": 5
          },
          "user": {
            "type": "integer",
            "description": "Employee (user) id (must belong to the company and not be in another group).",
            "example": 44
          },
          "penalty": {
            "type": "integer",
            "minimum": 1,
            "description": "Priority (1 = highest; values below 2 are stored as 1).",
            "example": 1
          }
        }
      },
      "GroupEmployeesBulkInput": {
        "type": "object",
        "description": "Bulk member list.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "description": "Payload.",
            "required": [
              "group_employees"
            ],
            "properties": {
              "group_employees": {
                "type": "array",
                "description": "Members to set (all with the same `group`).",
                "items": {
                  "type": "object",
                  "description": "Member.",
                  "required": [
                    "group",
                    "user"
                  ],
                  "properties": {
                    "group": {
                      "type": "integer",
                      "description": "Group id.",
                      "example": 5
                    },
                    "user": {
                      "type": "integer",
                      "description": "Employee (user) id.",
                      "example": 44
                    }
                  }
                }
              }
            }
          }
        }
      },
      "GroupEmployeesBulkResult": {
        "type": "object",
        "description": "Bulk result.",
        "properties": {
          "success": {
            "type": "array",
            "description": "Created memberships (with assigned `penalty`).",
            "items": {
              "type": "object",
              "description": "Membership.",
              "properties": {
                "group": {
                  "type": "integer",
                  "description": "Group id.",
                  "example": 5
                },
                "user": {
                  "type": "integer",
                  "description": "User id.",
                  "example": 44
                },
                "penalty": {
                  "type": "integer",
                  "description": "Assigned priority.",
                  "example": 1
                }
              }
            }
          },
          "failed": {
            "type": "array",
            "description": "Entries that were not created (always empty currently).",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "NotifPersonalItem": {
        "type": "object",
        "description": "Personal notification.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Notification id.",
            "example": 501
          },
          "serviceName": {
            "type": "string",
            "description": "Delivery channel (always fcm in the inbox).",
            "example": "fcm"
          },
          "isSend": {
            "type": "boolean",
            "description": "Push was sent.",
            "example": true
          },
          "isRead": {
            "type": "boolean",
            "description": "Read by the user.",
            "example": false
          },
          "readAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When read.",
            "example": null
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-02-02T07:00:05.193Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-02-02T07:00:05.193Z"
          },
          "templateName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Event template, e.g. expiresPhoneAfter3Days, orderStatusChanged, lowBalance, missedCall.",
            "example": "expiresPhoneAfter3Days"
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rendered title in the user's language.",
            "example": "Number 380441234567 expires in 3 days"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rendered text.",
            "example": "Top up 229 ₴ to renew the number"
          },
          "payload": {
            "type": "object",
            "description": "Event fields for deep links: templateName, title, description, orderId, orderStatus, amount, phone, subscriptionId, msgId, status, redirect_link, created_at_time, fcmData.",
            "example": {
              "templateName": "expiresPhoneAfter3Days",
              "amount": 229,
              "phone": "380441234567"
            }
          }
        }
      },
      "NotifNewsItem": {
        "type": "object",
        "description": "News / announcement with the user's state.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "News id.",
            "example": 5
          },
          "title": {
            "type": "string",
            "description": "Title.",
            "example": "New eSIM bundles"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Short text.",
            "example": "Regional eSIM bundles are now available."
          },
          "body": {
            "type": [
              "string",
              "null"
            ],
            "description": "Full text (Markdown).",
            "example": "Regional **eSIM** bundles are now available in the cabinet."
          },
          "image": {
            "type": [
              "object",
              "null"
            ],
            "description": "Image (Strapi upload file: url, formats, …) or null.",
            "example": null
          },
          "publishedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Publication date.",
            "example": "2026-02-01T14:12:28.987Z"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-02-01T14:12:27.857Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-02-01T14:12:28.992Z"
          },
          "isRead": {
            "type": "boolean",
            "description": "Read by the user.",
            "example": false
          },
          "readAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When read.",
            "example": null
          },
          "isHidden": {
            "type": "boolean",
            "description": "Hidden by the user.",
            "example": false
          },
          "hiddenAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When hidden.",
            "example": null
          }
        }
      },
      "NotifReadRequest": {
        "type": "object",
        "description": "Which items to mark: either `ids` or `all: true`.",
        "properties": {
          "ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Ids to mark as read.",
            "example": [
              501,
              502
            ]
          },
          "all": {
            "type": "boolean",
            "description": "Mark all unread.",
            "example": false
          }
        }
      },
      "NotifUpdatedResult": {
        "type": "object",
        "description": "Count of updated items.",
        "properties": {
          "data": {
            "type": "object",
            "description": "Result.",
            "properties": {
              "updated": {
                "type": "integer",
                "description": "Number of items marked.",
                "example": 2
              }
            }
          }
        }
      },
      "NotifHiddenResult": {
        "type": "object",
        "description": "Count of hidden items.",
        "properties": {
          "data": {
            "type": "object",
            "description": "Result.",
            "properties": {
              "hidden": {
                "type": "integer",
                "description": "Number of news hidden.",
                "example": 1
              }
            }
          }
        }
      },
      "NotifContact": {
        "type": "object",
        "description": "Notification contact (component).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id (keep it when updating).",
            "example": 1
          },
          "type": {
            "type": "string",
            "enum": [
              "email",
              "telegram"
            ],
            "description": "Channel.",
            "example": "email"
          },
          "contact": {
            "type": "string",
            "description": "E-mail address or Telegram chat id.",
            "example": "owner@example.com"
          },
          "finance": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Receive finance notifications (payments, low balance, expiring services).",
            "example": true
          },
          "call": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Receive missed-call notifications.",
            "example": true
          },
          "widgets": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Receive callback-widget requests and the daily widget digest.",
            "example": true
          },
          "statistics": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Receive the statistics digest.",
            "example": true
          },
          "lang": {
            "type": [
              "string",
              "null"
            ],
            "description": "Language of messages: en, ru-RU, uk-UA.",
            "example": "en"
          },
          "tgUsername": {
            "type": [
              "string",
              "null"
            ],
            "description": "Telegram username (filled by the bot).",
            "example": null
          },
          "tgFirstname": {
            "type": [
              "string",
              "null"
            ],
            "description": "Telegram first name (filled by the bot).",
            "example": null
          }
        }
      },
      "NotifSettingInput": {
        "type": "object",
        "description": "Notification settings fields.",
        "properties": {
          "send_notification_at": {
            "type": "string",
            "description": "Time of the daily digest, `HH:mm:ss` (Kyiv time).",
            "example": "09:00:00"
          },
          "minimum_balance": {
            "type": "number",
            "description": "Low-balance threshold in the company currency; a notification is sent when the balance drops below it.",
            "example": 100
          },
          "notified_minimum_balance": {
            "type": "boolean",
            "description": "Enable the low-balance notification.",
            "example": true
          },
          "notification_contacts": {
            "type": "array",
            "description": "Full list of contacts (contacts must be unique).",
            "items": {
              "$ref": "#/components/schemas/NotifContact"
            }
          }
        }
      },
      "NotifSetting": {
        "type": "object",
        "description": "Notification settings record.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Settings id.",
            "example": 11
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "send_notification_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Daily digest time `HH:mm:ss.SSS` (Kyiv time).",
                "example": "09:00:00.000"
              },
              "minimum_balance": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Low-balance threshold.",
                "example": 100
              },
              "notified_minimum_balance": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Low-balance notification enabled.",
                "example": null
              },
              "is_minimum_balance_notified": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Server flag: the low-balance notification has already been sent for the current drop.",
                "example": false
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T10:00:00.000Z"
              },
              "notification_contacts": {
                "type": "array",
                "description": "Contacts (present when populated).",
                "items": {
                  "$ref": "#/components/schemas/NotifContact"
                }
              }
            }
          }
        }
      },
      "NotifPushTokenFlat": {
        "type": "object",
        "description": "FCM token record (flat).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Record id.",
            "example": 845
          },
          "token": {
            "type": "string",
            "description": "FCM registration token.",
            "example": "cXYZ123_example-installation-id:APA91bExampleTokenBody"
          },
          "user_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Device label.",
            "example": "John's iPhone"
          },
          "device_os": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "android",
              "ios",
              "web",
              null
            ],
            "description": "Platform.",
            "example": "ios"
          },
          "device_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Device identifier.",
            "example": "0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-02-02T09:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-02-02T09:00:00.000Z"
          }
        }
      },
      "NotifPushToken": {
        "type": "object",
        "description": "FCM token record (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Record id.",
            "example": 845
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "token": {
                "type": "string",
                "description": "FCM registration token.",
                "example": "cXYZ123_example-installation-id:APA91bExampleTokenBody"
              },
              "user_label": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Device label.",
                "example": "John's iPhone"
              },
              "device_os": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "android",
                  "ios",
                  "web",
                  null
                ],
                "description": "Platform.",
                "example": "ios"
              },
              "device_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Device identifier.",
                "example": "0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-02-02T09:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-02T09:00:00.000Z"
              }
            }
          }
        }
      },
      "NumbersPhoneAttributes": {
        "type": "object",
        "description": "Number attributes. Classes (`type`): `simple` — regular number, then `bronze`, `silver`, `golden`, `platinum`, `exclusive` — increasingly \"beautiful\" (memorable) numbers with higher price; `exclusive` is also used for external (own) numbers. `channels` — how many simultaneous calls the number can carry. `reserved` — the number is locked by a purchase in progress and is hidden from the marketplace. `verificationRequired` — the regulator demands owner verification; until `verifiedAt` is set the number is not active for calls. `fullnumber` — full number in E.164 without plus (`country_prefix.prefix + phone_prefix.prefix + number`).",
        "properties": {
          "number": {
            "type": "string",
            "description": "Local part of the number (after the country and prefix codes).",
            "example": "1234567"
          },
          "fullnumber": {
            "type": "string",
            "description": "Full number in E.164 without the plus sign.",
            "example": "380441234567"
          },
          "channels": {
            "type": "integer",
            "description": "Simultaneous calls limit.",
            "example": 4
          },
          "type": {
            "type": "string",
            "enum": [
              "simple",
              "bronze",
              "silver",
              "golden",
              "platinum",
              "exclusive"
            ],
            "description": "Number class.",
            "example": "simple"
          },
          "reserved": {
            "type": "boolean",
            "description": "Locked by a purchase in progress / pending verification; hidden from the marketplace.",
            "example": false
          },
          "verificationRequired": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Owner verification is required by the regulator.",
            "example": false
          },
          "verifiedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When verification was completed; null — not verified (or not needed).",
            "example": null
          },
          "priority": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Display/selection priority.",
            "example": 3
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-01-15T10:00:00.000Z"
          }
        }
      },
      "NumbersPhone": {
        "type": "object",
        "description": "Number (Strapi envelope). Relations appear only when requested via `populate`.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Number id.",
            "example": 35
          },
          "attributes": {
            "description": "Attributes with optional relations.",
            "allOf": [
              {
                "$ref": "#/components/schemas/NumbersPhoneAttributes"
              },
              {
                "type": "object",
                "description": "Optional relations and extras.",
                "properties": {
                  "country_prefix": {
                    "type": "object",
                    "description": "Country (`populate`).",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/NumbersCountryPrefix"
                      }
                    }
                  },
                  "phone_prefix": {
                    "type": "object",
                    "description": "Prefix (`populate`).",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/NumbersPhonePrefix"
                      }
                    }
                  },
                  "inSip": {
                    "type": "object",
                    "description": "SIP line receiving incoming calls (`populate`); `data: null` if none.",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/NumbersSipLineRef"
                      }
                    }
                  },
                  "in_group": {
                    "type": "object",
                    "description": "Call group receiving incoming calls (`populate`).",
                    "properties": {
                      "data": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "description": "Group entity."
                      }
                    }
                  },
                  "in_ivr_line": {
                    "type": "object",
                    "description": "IVR receiving incoming calls (`populate`).",
                    "properties": {
                      "data": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "description": "IVR entity."
                      }
                    }
                  },
                  "out_sip_lines": {
                    "type": "object",
                    "description": "SIP lines using this number for outgoing calls (`populate`).",
                    "properties": {
                      "data": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/NumbersSipLineRef"
                        },
                        "description": "SIP lines."
                      }
                    }
                  },
                  "out_group_lines": {
                    "type": "object",
                    "description": "Call groups using this number for outgoing calls (`populate`).",
                    "properties": {
                      "data": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        },
                        "description": "Groups."
                      }
                    }
                  },
                  "out_ivr_lines": {
                    "type": "object",
                    "description": "IVR menus using this number for outgoing calls (`populate`).",
                    "properties": {
                      "data": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        },
                        "description": "IVRs."
                      }
                    }
                  },
                  "sip_trunk": {
                    "type": "object",
                    "description": "SIP trunk for external numbers (`populate`); `data: null` for platform numbers.",
                    "properties": {
                      "data": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "description": "Trunk entity."
                      }
                    }
                  },
                  "prices": {
                    "type": "object",
                    "description": "Prices of the number (`populate`).",
                    "properties": {
                      "data": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/NumbersPrice"
                        },
                        "description": "Prices."
                      }
                    }
                  },
                  "balances": {
                    "type": "object",
                    "description": "Only with `populate[balances]=true`: remaining talk-packet minutes grouped by expiration date (ISO-8601 key).",
                    "additionalProperties": {
                      "type": "array",
                      "description": "Balances expiring at this date.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersTalkPacketBalance"
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "NumbersTalkPacketBalance": {
        "type": "object",
        "description": "Remaining seconds of a talk packet side.",
        "properties": {
          "side": {
            "type": "string",
            "enum": [
              "in",
              "out"
            ],
            "description": "`in` — incoming, `out` — outgoing minutes.",
            "example": "out"
          },
          "seconds_left": {
            "type": "number",
            "description": "Seconds left.",
            "example": 1200
          },
          "total": {
            "type": "integer",
            "description": "Seconds included in the packet.",
            "example": 6000
          }
        }
      },
      "NumbersMarketplacePhone": {
        "type": "object",
        "description": "Free number on the marketplace with country, prefix (owner, prices, names) and own prices always included.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Number id.",
            "example": 101
          },
          "attributes": {
            "description": "Attributes with marketplace extras.",
            "allOf": [
              {
                "$ref": "#/components/schemas/NumbersPhoneAttributes"
              },
              {
                "type": "object",
                "description": "Marketplace extras.",
                "properties": {
                  "prefix_type": {
                    "type": "string",
                    "enum": [
                      "",
                      "0800",
                      "urban",
                      "mobiles"
                    ],
                    "description": "Legacy owner type: `0800` — national, `urban` — city, `mobiles` — mobile.",
                    "example": "urban"
                  },
                  "company": {
                    "type": "object",
                    "description": "Always `data: null` for free numbers.",
                    "properties": {
                      "data": {
                        "type": "null",
                        "description": "Null."
                      }
                    }
                  },
                  "country_prefix": {
                    "type": "object",
                    "description": "Country.",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/NumbersCountryPrefix"
                      }
                    }
                  },
                  "phone_prefix": {
                    "type": "object",
                    "description": "Prefix with owner.",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/NumbersPhonePrefix"
                      }
                    }
                  },
                  "prices": {
                    "type": "object",
                    "description": "Prices of this specific number (one per currency). `price` — one-time price of a beautiful number / monthly fee.",
                    "properties": {
                      "data": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/NumbersPrice"
                        },
                        "description": "Prices."
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "NumbersPhoneFlat": {
        "type": "object",
        "description": "Number as a flat object (custom endpoints): the same attributes plus populated relations without the `data` wrapper.",
        "allOf": [
          {
            "type": "object",
            "description": "Id.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Number id (absent inside `attributes` of `GET /companies/phone/{id}`).",
                "example": 35
              }
            }
          },
          {
            "$ref": "#/components/schemas/NumbersPhoneAttributes"
          },
          {
            "type": "object",
            "description": "Populated relations.",
            "properties": {
              "country_prefix": {
                "$ref": "#/components/schemas/NumbersCountryPrefixFlat"
              },
              "inSip": {
                "description": "Incoming SIP line or null.",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/NumbersSipLineFlat"
                  },
                  {
                    "type": "null",
                    "description": "No incoming SIP line."
                  }
                ]
              },
              "in_group": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Incoming call group."
              },
              "in_ivr_line": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Incoming IVR."
              },
              "out_sip_lines": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/NumbersSipLineFlat"
                },
                "description": "Outgoing SIP lines."
              },
              "out_group_lines": {
                "type": "array",
                "items": {
                  "type": "object"
                },
                "description": "Outgoing call groups."
              },
              "out_ivr_lines": {
                "type": "array",
                "items": {
                  "type": "object"
                },
                "description": "Outgoing IVRs."
              }
            }
          }
        ]
      },
      "NumbersSipLineFlat": {
        "type": "object",
        "description": "SIP line (short). `password` is returned by the server but must be treated as a secret.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "SIP line id.",
            "example": 12
          },
          "number": {
            "type": "string",
            "description": "Internal SIP number (extension).",
            "example": "010"
          },
          "password": {
            "type": "string",
            "description": "SIP password (secret).",
            "example": "********"
          },
          "record_call": {
            "type": "boolean",
            "description": "Call recording enabled.",
            "example": true
          },
          "call_forwarding": {
            "type": "string",
            "description": "Call forwarding mode.",
            "example": "off"
          },
          "redirectActivity": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Redirect active.",
            "example": false
          },
          "redirectTo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Redirect target.",
            "example": null
          },
          "user": {
            "type": "object",
            "description": "Employee owning the line (when populated).",
            "properties": {
              "id": {
                "type": "integer",
                "description": "User id.",
                "example": 7
              },
              "username": {
                "type": "string",
                "description": "Display name.",
                "example": "John Doe"
              },
              "email": {
                "type": "string",
                "description": "Email.",
                "example": "john@example.com"
              }
            }
          }
        }
      },
      "NumbersSipLineRef": {
        "type": "object",
        "description": "SIP line in Strapi envelope.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "SIP line id.",
            "example": 12
          },
          "attributes": {
            "$ref": "#/components/schemas/NumbersSipLineFlat"
          }
        }
      },
      "NumbersLineAssignRequest": {
        "type": "object",
        "description": "Line assignment request.",
        "required": [
          "phone",
          "data"
        ],
        "properties": {
          "phone": {
            "type": "integer",
            "description": "Id of a number of your company.",
            "example": 35
          },
          "data": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "description": "Exactly two items: `[kind, relation]`. `kind` — `sip`, `group` or `ivr`; `relation` — `{ \"connect\": [ids], \"disconnect\": [ids] }`.",
            "items": {
              "description": "Kind string or relation object.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "sip",
                    "group",
                    "ivr"
                  ],
                  "description": "Kind."
                },
                {
                  "type": "object",
                  "description": "Relation payload.",
                  "properties": {
                    "connect": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      },
                      "description": "Ids to attach.",
                      "example": [
                        12
                      ]
                    },
                    "disconnect": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      },
                      "description": "Ids to detach (required, may be empty).",
                      "example": []
                    }
                  }
                }
              ]
            },
            "example": [
              "sip",
              {
                "connect": [
                  12
                ],
                "disconnect": []
              }
            ]
          }
        }
      },
      "NumbersCurrency": {
        "type": "object",
        "description": "Currency (Strapi envelope).",
        "properties": {
          "data": {
            "type": "object",
            "description": "Currency entity.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Currency id.",
                "example": 3
              },
              "attributes": {
                "type": "object",
                "description": "Currency attributes.",
                "properties": {
                  "name_short": {
                    "type": "string",
                    "description": "ISO code.",
                    "example": "UAH"
                  },
                  "name_long": {
                    "type": "string",
                    "description": "Name.",
                    "example": "Гривня"
                  },
                  "symbol": {
                    "type": "string",
                    "description": "Symbol.",
                    "example": "₴"
                  },
                  "prefix": {
                    "type": "string",
                    "description": "Symbol prefix when formatting.",
                    "example": ""
                  },
                  "suffix": {
                    "type": "string",
                    "description": "Symbol suffix when formatting.",
                    "example": ""
                  }
                }
              }
            }
          }
        }
      },
      "NumbersPriceByField": {
        "type": "object",
        "description": "One price component.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id.",
            "example": 1
          },
          "name": {
            "type": "string",
            "enum": [
              "price",
              "setup_price",
              "phone_channel_price",
              "domain_cost",
              "recall_widget_cost",
              "callback_widget_cost",
              "per_trunk_price",
              "sms_incoming_price",
              "sms_outgoing_price"
            ],
            "description": "`price` — monthly fee (or one-time price of a beautiful number), `setup_price` — connection, `phone_channel_price` — extra channel per month, `sms_incoming_price`/`sms_outgoing_price` — per SMS.",
            "example": "price"
          },
          "price": {
            "type": "number",
            "description": "Amount in the price currency.",
            "example": 229
          },
          "isPriceStatic": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Price is fixed (not recalculated from exchange rates).",
            "example": false
          }
        }
      },
      "NumbersPrice": {
        "type": "object",
        "description": "Price record: one currency, several components.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Price id.",
            "example": 501
          },
          "attributes": {
            "type": "object",
            "description": "Price attributes.",
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Internal description.",
                "example": "Virtual numbers price"
              },
              "currency": {
                "$ref": "#/components/schemas/NumbersCurrency"
              },
              "price_by_field": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/NumbersPriceByField"
                },
                "description": "Price components."
              }
            }
          }
        }
      },
      "NumbersCountryPrefixAttributes": {
        "type": "object",
        "description": "Country attributes.",
        "properties": {
          "country": {
            "type": "string",
            "description": "ISO-2 country code.",
            "example": "UA"
          },
          "iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO-2 country code (duplicate of `country`).",
            "example": "UA"
          },
          "prefix": {
            "type": "string",
            "description": "Dialling code without plus.",
            "example": "380"
          },
          "isAvailable": {
            "type": "boolean",
            "description": "Numbers of this country can be bought.",
            "example": true
          },
          "outgoing_calls": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Outgoing calls supported.",
            "example": true
          },
          "incoming_calls": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Incoming calls supported.",
            "example": true
          },
          "outgoing_sms": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Outgoing SMS supported.",
            "example": false
          },
          "incoming_sms": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Incoming SMS supported.",
            "example": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-01-15T10:00:00.000Z"
          }
        }
      },
      "NumbersCountryPrefix": {
        "type": "object",
        "description": "Country (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Country id.",
            "example": 1
          },
          "attributes": {
            "$ref": "#/components/schemas/NumbersCountryPrefixAttributes"
          }
        }
      },
      "NumbersCountryPrefixFlat": {
        "allOf": [
          {
            "type": "object",
            "description": "Id.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Country id.",
                "example": 1
              }
            }
          },
          {
            "$ref": "#/components/schemas/NumbersCountryPrefixAttributes"
          }
        ],
        "description": "Country as a flat object."
      },
      "NumbersPhonePrefixOwner": {
        "type": "object",
        "description": "Prefix owner: city, mobile operator, national (0800) or toll-free range.",
        "properties": {
          "data": {
            "type": "object",
            "description": "Owner entity.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Owner id (use as `phonesCity` in `GET /phones`).",
                "example": 1
              },
              "attributes": {
                "type": "object",
                "description": "Owner attributes.",
                "properties": {
                  "owner": {
                    "type": "string",
                    "description": "Name (default locale).",
                    "example": "м. Київ"
                  },
                  "type": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "national",
                      "city",
                      "mobile",
                      "toll-free",
                      "shared",
                      null
                    ],
                    "description": "Owner type.",
                    "example": "city"
                  },
                  "available_call_directions": {
                    "type": "string",
                    "enum": [
                      "both",
                      "incomming",
                      "outgoing"
                    ],
                    "description": "Allowed call directions for numbers of this owner.",
                    "example": "both"
                  },
                  "country_prefix": {
                    "type": "object",
                    "description": "Country (`populate`).",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/NumbersCountryPrefix"
                      }
                    }
                  },
                  "names": {
                    "type": "array",
                    "description": "Localized names.",
                    "items": {
                      "type": "object",
                      "description": "Name in a locale.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Component id.",
                          "example": 1
                        },
                        "locales": {
                          "type": "string",
                          "enum": [
                            "uk-UA",
                            "ru",
                            "en",
                            "pl"
                          ],
                          "description": "Locale.",
                          "example": "en"
                        },
                        "name": {
                          "type": "string",
                          "description": "Name.",
                          "example": "Kyiv"
                        }
                      }
                    }
                  },
                  "prices": {
                    "type": "object",
                    "description": "Owner-level prices (`populate`).",
                    "properties": {
                      "data": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/NumbersPrice"
                        },
                        "description": "Prices."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "NumbersTalkPacket": {
        "type": "object",
        "description": "Talk packet (bundle of minutes) available for numbers of the prefix.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Packet id.",
            "example": 3
          },
          "attributes": {
            "type": "object",
            "description": "Packet attributes.",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "small",
                  "medium",
                  "large"
                ],
                "description": "Packet size.",
                "example": "small"
              },
              "incomming_seconds_included": {
                "type": "string",
                "description": "Incoming seconds included (big integer as string).",
                "example": "60000"
              },
              "outgoing_seconds_included": {
                "type": "string",
                "description": "Outgoing seconds included (big integer as string).",
                "example": "6000"
              },
              "prices": {
                "type": "object",
                "description": "Packet prices per currency (`populate`).",
                "properties": {
                  "data": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/NumbersPrice"
                    },
                    "description": "Prices."
                  }
                }
              }
            }
          }
        }
      },
      "NumbersPhonePrefix": {
        "type": "object",
        "description": "Number prefix (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Prefix id.",
            "example": 9
          },
          "attributes": {
            "type": "object",
            "description": "Prefix attributes.",
            "properties": {
              "prefix": {
                "type": "string",
                "description": "Prefix digits after the country code.",
                "example": "44"
              },
              "capacity": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Capacity of the range (informational).",
                "example": null
              },
              "validation": {
                "type": "boolean",
                "description": "Owner verification is required for numbers of this prefix.",
                "example": false
              },
              "call": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "NO",
                  "INT",
                  "LOC",
                  null
                ],
                "description": "Outgoing calls: `NO` — not allowed, `INT` — international, `LOC` — local only.",
                "example": "INT"
              },
              "incoming_sms": {
                "type": "boolean",
                "description": "Incoming SMS supported.",
                "example": false
              },
              "outgoing_sms": {
                "type": "boolean",
                "description": "Outgoing SMS supported.",
                "example": false
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "phone_prefix_owner": {
                "$ref": "#/components/schemas/NumbersPhonePrefixOwner"
              },
              "prices": {
                "type": "object",
                "description": "Prices of numbers in this prefix, one per currency (`populate`).",
                "properties": {
                  "data": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/NumbersPrice"
                    },
                    "description": "Prices."
                  }
                }
              },
              "talk_packets": {
                "type": "object",
                "description": "Talk packets available for the prefix (`populate`).",
                "properties": {
                  "data": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/NumbersTalkPacket"
                    },
                    "description": "Packets."
                  }
                }
              },
              "validation_fileds": {
                "type": "array",
                "items": {
                  "type": "object"
                },
                "description": "Extra fields requested during validation (usually empty).",
                "example": []
              }
            }
          }
        }
      },
      "NumbersCatalogCountry": {
        "type": "object",
        "description": "Country summary of the international numbers catalogue.",
        "properties": {
          "country": {
            "type": "string",
            "description": "Country name.",
            "example": "United States"
          },
          "iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO-2 code.",
            "example": "US"
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Dialling code.",
            "example": "1"
          },
          "groups": {
            "type": "integer",
            "description": "Number of prefix groups.",
            "example": 320
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Number types available.",
            "example": [
              "Local",
              "Toll-free"
            ]
          },
          "monthly_from": {
            "type": [
              "number",
              "null"
            ],
            "description": "Lowest monthly price, USD.",
            "example": 3.99
          },
          "monthly_to": {
            "type": [
              "number",
              "null"
            ],
            "description": "Highest monthly price, USD.",
            "example": 24.99
          },
          "setup_from": {
            "type": "number",
            "description": "Connection fee (always 0 — included in the monthly price).",
            "example": 0
          },
          "in_stock": {
            "type": "boolean",
            "description": "Numbers are in stock.",
            "example": true
          },
          "verification": {
            "type": "string",
            "enum": [
              "none",
              "partial",
              "all"
            ],
            "description": "Owner verification: `none` — not needed, `all` — needed for every group, `partial` — depends on the group.",
            "example": "partial"
          },
          "verification_groups": {
            "type": "integer",
            "description": "Groups that need verification.",
            "example": 12
          },
          "monthly_from_no_verification": {
            "type": [
              "number",
              "null"
            ],
            "description": "Lowest monthly price among groups without verification, USD.",
            "example": 3.99
          }
        }
      },
      "NumbersCatalogGroup": {
        "type": "object",
        "description": "Group of international numbers (country + prefix/area).",
        "properties": {
          "id": {
            "type": "string",
            "description": "Group id at the provider (UUID).",
            "example": "0f1e2d3c-0000-4000-8000-000000000001"
          },
          "country": {
            "type": "string",
            "description": "Country name.",
            "example": "United States"
          },
          "iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO-2 code.",
            "example": "US"
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Dialling code.",
            "example": "1"
          },
          "prefix": {
            "type": "string",
            "description": "Area prefix after the country code.",
            "example": "212"
          },
          "area": {
            "type": [
              "string",
              "null"
            ],
            "description": "Area name.",
            "example": "New York"
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "City.",
            "example": "New York"
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "Region/state.",
            "example": "NY"
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Number type (`Local`, `National`, `Mobile`, `Toll-free`...).",
            "example": "Local"
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Features: `voice_in`, `voice_out`, `sms_in`, `sms_out`, `t38`.",
            "example": [
              "voice_in",
              "voice_out",
              "sms_in"
            ]
          },
          "monthly": {
            "type": [
              "number",
              "null"
            ],
            "description": "Monthly price, USD.",
            "example": 3.99
          },
          "setup": {
            "type": "number",
            "description": "Connection fee (always 0).",
            "example": 0
          },
          "in_stock": {
            "type": "boolean",
            "description": "In stock.",
            "example": true
          },
          "needs_verification": {
            "type": "boolean",
            "description": "Owner verification required before activation.",
            "example": false
          }
        }
      },
      "NumbersCatalogRateCountry": {
        "type": "object",
        "description": "Per-country summary of outgoing rates.",
        "properties": {
          "country": {
            "type": "string",
            "description": "Country name.",
            "example": "United States"
          },
          "iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO-2 code.",
            "example": "US"
          },
          "prefixes": {
            "type": "integer",
            "description": "Number of prefixes.",
            "example": 25
          },
          "price_from": {
            "type": [
              "number",
              "null"
            ],
            "description": "Lowest rate, US cents per minute.",
            "example": 2
          },
          "price_to": {
            "type": [
              "number",
              "null"
            ],
            "description": "Highest rate, US cents per minute.",
            "example": 15
          }
        }
      },
      "NumbersCatalogRate": {
        "type": "object",
        "description": "Outgoing rate for a destination prefix.",
        "properties": {
          "prefix": {
            "type": "string",
            "description": "Destination prefix (with country code).",
            "example": "1212"
          },
          "country": {
            "type": "string",
            "description": "Country name.",
            "example": "United States"
          },
          "iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO-2 code.",
            "example": "US"
          },
          "network": {
            "type": [
              "string",
              "null"
            ],
            "description": "Network / operator.",
            "example": "US Fixed"
          },
          "route": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "international",
              "local",
              null
            ],
            "description": "Route type.",
            "example": "international"
          },
          "price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Rate, US cents per minute.",
            "example": 2
          },
          "increments": {
            "type": "object",
            "description": "Billing increments in seconds.",
            "properties": {
              "initial": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "First billed interval.",
                "example": 1
              },
              "recurring": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Subsequent intervals.",
                "example": 1
              }
            }
          }
        }
      },
      "NumbersProofEntry": {
        "type": "object",
        "description": "Journal entry of an uploaded proof document (files themselves are not stored).",
        "properties": {
          "didww_proof_id": {
            "type": "string",
            "description": "Proof id at the provider.",
            "example": "9a1b2c3d-0000-4000-8000-000000000001"
          },
          "proof_type_id": {
            "type": "string",
            "description": "Proof type id.",
            "example": "7f0e1d2c-0000-4000-8000-000000000002"
          },
          "files": {
            "type": "array",
            "description": "Uploaded file names.",
            "items": {
              "type": "object",
              "description": "File.",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "File name.",
                  "example": "passport.pdf"
                }
              }
            }
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Proof expiry at the provider.",
            "example": "2027-01-15T00:00:00.000Z"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Uploaded at.",
            "example": "2026-01-15T10:00:00.000Z"
          }
        }
      },
      "NumbersSupportingDocumentEntry": {
        "type": "object",
        "description": "Journal entry of a permanent supporting document.",
        "properties": {
          "didww_document_id": {
            "type": "string",
            "description": "Document id at the provider.",
            "example": "3c2b1a09-0000-4000-8000-000000000006"
          },
          "template_id": {
            "type": "string",
            "description": "Template id.",
            "example": "5e4d3c2b-0000-4000-8000-000000000005"
          },
          "files": {
            "type": "array",
            "description": "Uploaded file names.",
            "items": {
              "type": "object",
              "description": "File.",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "File name.",
                  "example": "loa-signed.pdf"
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Uploaded at.",
            "example": "2026-01-15T10:20:00.000Z"
          }
        }
      },
      "NumbersProofUpload": {
        "type": "object",
        "description": "Multipart proof upload.",
        "required": [
          "files",
          "proof_type_id"
        ],
        "properties": {
          "files": {
            "type": "string",
            "format": "binary",
            "description": "One or more files (JPG, PNG, PDF, max 10 MB each). Repeat the field for several files."
          },
          "proof_type_id": {
            "type": "string",
            "description": "Proof type id from the requirements.",
            "example": "7f0e1d2c-0000-4000-8000-000000000002"
          }
        }
      },
      "NumbersAddress": {
        "type": "object",
        "description": "Address of an identity.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Address id.",
            "example": 8
          },
          "country_iso": {
            "type": "string",
            "description": "ISO-2 country code.",
            "example": "UA"
          },
          "city_name": {
            "type": "string",
            "description": "City.",
            "example": "Kyiv"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code.",
            "example": "01001"
          },
          "address": {
            "type": "string",
            "description": "Street address.",
            "example": "1 Example St"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Note.",
            "example": null
          },
          "verified": {
            "type": "boolean",
            "description": "Approved by the provider's compliance in some verification.",
            "example": false
          },
          "proofs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumbersProofEntry"
            },
            "description": "Uploaded address proofs."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-15T10:05:00.000Z"
          }
        }
      },
      "NumbersIdentity": {
        "type": "object",
        "description": "Owner identity (person or business) for number verification.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identity id.",
            "example": 5
          },
          "identity_type": {
            "type": "string",
            "enum": [
              "personal",
              "business"
            ],
            "description": "Person or business.",
            "example": "business"
          },
          "first_name": {
            "type": "string",
            "description": "First name (of the person or the company representative).",
            "example": "John"
          },
          "last_name": {
            "type": "string",
            "description": "Last name.",
            "example": "Doe"
          },
          "phone_number": {
            "type": "string",
            "description": "Contact phone.",
            "example": "+380441234567"
          },
          "company_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Company name (business).",
            "example": "Example LLC"
          },
          "company_reg_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Company registration number.",
            "example": "12345678"
          },
          "vat_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "VAT id.",
            "example": null
          },
          "birth_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Birth date (personal).",
            "example": null
          },
          "id_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID document number.",
            "example": null
          },
          "personal_tax_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Personal tax id.",
            "example": null
          },
          "contact_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "Contact email.",
            "example": "owner@example.com"
          },
          "country_iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "Country of residence/registration, ISO-2.",
            "example": "UA"
          },
          "birth_country_iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "Country of birth, ISO-2.",
            "example": null
          },
          "verified": {
            "type": "boolean",
            "description": "Approved by the provider's compliance in some verification.",
            "example": false
          },
          "proofs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumbersProofEntry"
            },
            "description": "Uploaded identity proofs."
          },
          "permanent_documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumbersSupportingDocumentEntry"
            },
            "description": "Uploaded permanent documents."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "addresses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumbersAddress"
            },
            "description": "Addresses (list method only)."
          }
        }
      },
      "NumbersIdentityInput": {
        "type": "object",
        "description": "Identity fields for creation.",
        "required": [
          "identity_type",
          "first_name",
          "last_name",
          "phone_number"
        ],
        "properties": {
          "identity_type": {
            "type": "string",
            "enum": [
              "personal",
              "business"
            ],
            "description": "Person or business.",
            "example": "business"
          },
          "first_name": {
            "type": "string",
            "description": "First name.",
            "example": "John"
          },
          "last_name": {
            "type": "string",
            "description": "Last name.",
            "example": "Doe"
          },
          "phone_number": {
            "type": "string",
            "description": "Contact phone.",
            "example": "+380441234567"
          },
          "company_name": {
            "type": "string",
            "description": "Company name (required for business).",
            "example": "Example LLC"
          },
          "company_reg_number": {
            "type": "string",
            "description": "Company registration number.",
            "example": "12345678"
          },
          "vat_id": {
            "type": "string",
            "description": "VAT id.",
            "example": "UA123456789"
          },
          "birth_date": {
            "type": "string",
            "format": "date",
            "description": "Birth date (personal).",
            "example": "1985-04-12"
          },
          "id_number": {
            "type": "string",
            "description": "ID document number.",
            "example": "AB123456"
          },
          "personal_tax_id": {
            "type": "string",
            "description": "Personal tax id.",
            "example": "1234567890"
          },
          "contact_email": {
            "type": "string",
            "format": "email",
            "description": "Contact email.",
            "example": "owner@example.com"
          },
          "country_iso": {
            "type": "string",
            "description": "Country ISO-2.",
            "example": "UA"
          },
          "birth_country_iso": {
            "type": "string",
            "description": "Birth country ISO-2.",
            "example": "UA"
          }
        }
      },
      "NumbersIdentityUpdate": {
        "type": "object",
        "description": "Updatable identity fields.",
        "properties": {
          "first_name": {
            "type": "string",
            "description": "First name.",
            "example": "John"
          },
          "last_name": {
            "type": "string",
            "description": "Last name.",
            "example": "Doe"
          },
          "phone_number": {
            "type": "string",
            "description": "Contact phone.",
            "example": "+380441234568"
          },
          "company_name": {
            "type": "string",
            "description": "Company name.",
            "example": "Example LLC"
          },
          "company_reg_number": {
            "type": "string",
            "description": "Company registration number.",
            "example": "12345678"
          },
          "vat_id": {
            "type": "string",
            "description": "VAT id.",
            "example": "UA123456789"
          },
          "birth_date": {
            "type": "string",
            "format": "date",
            "description": "Birth date.",
            "example": "1985-04-12"
          },
          "id_number": {
            "type": "string",
            "description": "ID document number.",
            "example": "AB123456"
          },
          "personal_tax_id": {
            "type": "string",
            "description": "Personal tax id.",
            "example": "1234567890"
          },
          "contact_email": {
            "type": "string",
            "format": "email",
            "description": "Contact email.",
            "example": "owner@example.com"
          }
        }
      },
      "NumbersOk": {
        "type": "object",
        "description": "Simple success result.",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Always true.",
            "example": true
          }
        }
      },
      "NumbersRequirementRef": {
        "type": "object",
        "description": "Reference to a proof type or document template.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id (use as `proof_type_id` / `template_id`).",
            "example": "7f0e1d2c-0000-4000-8000-000000000002"
          },
          "name": {
            "type": "string",
            "description": "Name.",
            "example": "Passport"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Template download URL (documents only).",
            "example": null
          }
        }
      },
      "NumbersRequirementSide": {
        "type": "object",
        "description": "Requirements for one identity kind.",
        "properties": {
          "area_level": {
            "type": [
              "string",
              "null"
            ],
            "description": "Required address area level (`country`, `area`, `city`...) or null.",
            "example": "country"
          },
          "proof_qty": {
            "type": "integer",
            "description": "How many proofs are required.",
            "example": 1
          },
          "mandatory_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Identity fields that must be filled.",
            "example": [
              "first_name",
              "last_name",
              "birth_date"
            ]
          },
          "proof_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumbersRequirementRef"
            },
            "description": "Accepted proof types."
          },
          "permanent_document": {
            "description": "Permanent document template, or null.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/NumbersRequirementRef"
              },
              {
                "type": "null",
                "description": "No permanent document needed."
              }
            ]
          },
          "onetime_document": {
            "description": "One-time document template, or null.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/NumbersRequirementRef"
              },
              {
                "type": "null",
                "description": "No one-time document needed."
              }
            ]
          }
        }
      },
      "NumbersRequirement": {
        "type": "object",
        "description": "Regulatory requirement for a number group.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Requirement id at the provider.",
            "example": "a1b2c3d4-0000-4000-8000-000000000007"
          },
          "identity_type": {
            "type": "string",
            "enum": [
              "personal",
              "business",
              "any"
            ],
            "description": "Which identity kinds are accepted.",
            "example": "any"
          },
          "service_description_required": {
            "type": "boolean",
            "description": "`service_description` must be provided in the verification request.",
            "example": false
          },
          "restriction_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Regulator's restriction text, if any.",
            "example": null
          },
          "personal": {
            "$ref": "#/components/schemas/NumbersRequirementSide"
          },
          "business": {
            "$ref": "#/components/schemas/NumbersRequirementSide"
          },
          "address": {
            "type": "object",
            "description": "Address proof rules.",
            "properties": {
              "area_level": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Required address area level.",
                "example": "country"
              },
              "proof_qty": {
                "type": "integer",
                "description": "How many address proofs are required.",
                "example": 1
              },
              "proof_types": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/NumbersRequirementRef"
                },
                "description": "Accepted address proof types."
              }
            }
          }
        }
      },
      "NumbersPendingPhone": {
        "type": "object",
        "description": "Number awaiting verification.",
        "properties": {
          "phone_id": {
            "type": "integer",
            "description": "Number id.",
            "example": 41
          },
          "fullnumber": {
            "type": "string",
            "description": "E.164 digits without plus.",
            "example": "15551234567"
          },
          "verification_status": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "approved",
              "rejected"
            ],
            "description": "Status of the latest request (`none` — not submitted yet).",
            "example": "pending"
          },
          "verification_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Latest request id.",
            "example": 3
          },
          "reject_reasons": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Rejection reasons of the latest request.",
            "example": null
          },
          "reject_comment": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rejection comment.",
            "example": null
          }
        }
      },
      "NumbersVerificationInput": {
        "type": "object",
        "description": "Verification request fields.",
        "required": [
          "phone_ids",
          "identity_id",
          "address_id"
        ],
        "properties": {
          "phone_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Ids of your numbers that need verification.",
            "example": [
              41
            ]
          },
          "identity_id": {
            "type": "integer",
            "description": "Identity id.",
            "example": 5
          },
          "address_id": {
            "type": "integer",
            "description": "Address id (must belong to the identity).",
            "example": 8
          },
          "service_description": {
            "type": "string",
            "description": "How the numbers will be used (required when `service_description_required`).",
            "example": "Customer support line"
          }
        }
      },
      "NumbersVerification": {
        "type": "object",
        "description": "Verification request.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Request id.",
            "example": 3
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected"
            ],
            "description": "Status.",
            "example": "pending"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider reference.",
            "example": "AV-000123"
          },
          "service_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Service description.",
            "example": "Customer support line"
          },
          "reject_reasons": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Rejection reasons.",
            "example": null
          },
          "reject_comment": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rejection comment.",
            "example": null
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-16T09:00:00.000Z"
          },
          "phones": {
            "type": "array",
            "description": "Numbers in the request.",
            "items": {
              "type": "object",
              "description": "Number.",
              "properties": {
                "id": {
                  "type": "integer",
                  "description": "Number id.",
                  "example": 41
                },
                "fullnumber": {
                  "type": "string",
                  "description": "E.164 digits without plus.",
                  "example": "15551234567"
                }
              }
            }
          },
          "identity": {
            "type": "object",
            "description": "Identity (short).",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Identity id.",
                "example": 5
              },
              "identity_type": {
                "type": "string",
                "enum": [
                  "personal",
                  "business"
                ],
                "description": "Kind.",
                "example": "business"
              },
              "first_name": {
                "type": "string",
                "description": "First name.",
                "example": "John"
              },
              "last_name": {
                "type": "string",
                "description": "Last name.",
                "example": "Doe"
              },
              "company_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Company name.",
                "example": "Example LLC"
              }
            }
          },
          "address": {
            "type": "object",
            "description": "Address (short).",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Address id.",
                "example": 8
              },
              "country_iso": {
                "type": "string",
                "description": "ISO-2 country code.",
                "example": "US"
              },
              "city_name": {
                "type": "string",
                "description": "City.",
                "example": "New York"
              },
              "address": {
                "type": "string",
                "description": "Street address.",
                "example": "1 Example St"
              }
            }
          }
        }
      },
      "NumbersExternalPhoneShort": {
        "type": "object",
        "description": "External number (short form).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Number id.",
            "example": 77
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "fullnumber": {
                "type": "string",
                "description": "E.164 digits without plus.",
                "example": "380441234567"
              },
              "verifiedAt": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Verification/activation time; null — awaiting verification.",
                "example": null
              }
            }
          }
        }
      },
      "OrdersPhoneInput": {
        "type": "object",
        "description": "Number line of an order.",
        "required": [
          "prefix_id",
          "quantity"
        ],
        "properties": {
          "prefix_id": {
            "type": "integer",
            "description": "Prefix id from the number catalogue (Numbers section). Defines country/city and price.",
            "example": 9
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "description": "How many numbers of this prefix to buy. Must be 1 for a renewal.",
            "example": 1
          },
          "talk_package_id": {
            "type": "integer",
            "description": "Talk packet id (minutes bundle) to add to the number. Required for a renewal of an owned number.",
            "example": 2
          },
          "phone_id": {
            "type": "integer",
            "description": "Id of an owned number: buy a talk packet for it instead of a new number (requires `talk_package_id`, `quantity: 1`).",
            "example": 25
          }
        }
      },
      "OrdersCalculateInput": {
        "type": "object",
        "description": "Order content for price calculation.",
        "properties": {
          "phones": {
            "type": "array",
            "description": "Numbers to buy or renew.",
            "items": {
              "$ref": "#/components/schemas/OrdersPhoneInput"
            }
          },
          "tariff": {
            "type": "integer",
            "description": "Tariff id to switch to.",
            "example": 1
          },
          "esim": {
            "type": "object",
            "description": "eSIM bundle: `{ id }` from the eSIM catalogue; for a top-up of an existing eSIM — `{ id, name, iccid, refills: [{ amount_days, amount_mb }] }`.",
            "additionalProperties": true,
            "example": {
              "id": 120
            }
          },
          "domains": {
            "type": "array",
            "description": "Ids of extra SIP domains above the tariff limit.",
            "items": {
              "type": "integer",
              "description": "Domain id."
            },
            "example": [
              5
            ]
          },
          "recall_widgets": {
            "type": "array",
            "description": "Ids of extra recall widgets.",
            "items": {
              "type": "integer",
              "description": "Widget id."
            }
          },
          "callback_widgets": {
            "type": "array",
            "description": "Ids of extra callback widgets.",
            "items": {
              "type": "integer",
              "description": "Widget id."
            }
          },
          "attachServices": {
            "type": "array",
            "description": "Extra services: `{ __component: \"services.sip-trunk\", data: { ...trunk fields } }`.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "Service item."
            }
          },
          "amount": {
            "type": "number",
            "description": "Top-up amount for calculation (in `POST /orders` use `addMoney`).",
            "example": 20
          }
        }
      },
      "OrdersCreateInput": {
        "type": "object",
        "description": "Order to create.",
        "required": [
          "payment_system"
        ],
        "properties": {
          "payment_system": {
            "type": "integer",
            "description": "`0` — pay from the balance; any positive number — pay by card (gateway chosen by IP country: UA → WayForPay, else Stripe).",
            "example": 0
          },
          "phones": {
            "type": "array",
            "description": "Numbers to buy or talk packets for owned numbers.",
            "items": {
              "$ref": "#/components/schemas/OrdersPhoneInput"
            }
          },
          "tariff": {
            "type": "integer",
            "description": "Tariff id to switch to.",
            "example": 1
          },
          "esim": {
            "type": "object",
            "description": "eSIM bundle `{ id }` or top-up `{ id, name, iccid, refills }`.",
            "additionalProperties": true,
            "example": {
              "id": 120
            }
          },
          "domains": {
            "type": "array",
            "description": "Ids of extra SIP domains.",
            "items": {
              "type": "integer",
              "description": "Domain id."
            }
          },
          "recall_widgets": {
            "type": "array",
            "description": "Ids of extra recall widgets.",
            "items": {
              "type": "integer",
              "description": "Widget id."
            }
          },
          "callback_widgets": {
            "type": "array",
            "description": "Ids of extra callback widgets.",
            "items": {
              "type": "integer",
              "description": "Widget id."
            }
          },
          "attachServices": {
            "type": "array",
            "description": "Extra services, e.g. `{ __component: \"services.sip-trunk\", data: {...} }`.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "Service item."
            }
          },
          "addMoney": {
            "type": "number",
            "minimum": 5,
            "description": "Balance top-up amount in the company currency (minimum 5).",
            "example": 20
          },
          "promoCode": {
            "type": "string",
            "description": "Promo code.",
            "example": "WELCOME10"
          },
          "auto_renewal": {
            "type": "boolean",
            "default": true,
            "description": "Enable monthly auto-renewal for the subscriptions created by this order.",
            "example": true
          },
          "paymentType": {
            "type": "string",
            "enum": [
              "intent"
            ],
            "description": "`intent` — mobile Stripe PaymentSheet flow (then call `POST /payment-system/stripe/payment-sheet`).",
            "example": "intent"
          },
          "additional_data": {
            "type": "object",
            "description": "Optional hints, e.g. `{ selected_payment_method: \"apple_pay\" | \"google_pay\" }`.",
            "additionalProperties": true
          }
        }
      },
      "OrdersAttachedPhone": {
        "type": "object",
        "description": "Number issued for the order.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Phone id (see Numbers).",
            "example": 25
          },
          "fullnumber": {
            "type": "string",
            "description": "Full number in international format without `+`.",
            "example": "380441234567"
          },
          "service": {
            "type": "string",
            "description": "`default` — from the own pool (available at once); `didww` — bought at the provider in the background.",
            "example": "default"
          }
        }
      },
      "OrdersPhoneLine": {
        "type": "object",
        "description": "Number line in `orderJSON.phones`.",
        "properties": {
          "prefix_id": {
            "type": "integer",
            "description": "Prefix id.",
            "example": 9
          },
          "prefix_number": {
            "type": "string",
            "description": "Prefix digits.",
            "example": "44"
          },
          "quantity": {
            "type": "integer",
            "description": "Numbers issued.",
            "example": 1
          },
          "setup_price": {
            "type": "number",
            "description": "Price of the number(s) for the first period (quantity included).",
            "example": 5.99
          },
          "talk_package_id": {
            "type": "integer",
            "description": "Talk packet id, if chosen.",
            "example": 2
          },
          "talk_package_price": {
            "type": "number",
            "description": "Talk packet price.",
            "example": 4
          },
          "phone_id": {
            "type": "integer",
            "description": "Owned number id for a renewal.",
            "example": 25
          },
          "isRenew": {
            "type": "boolean",
            "description": "true — talk packet / renewal for an owned number.",
            "example": false
          },
          "attached_phones": {
            "type": "array",
            "description": "Issued numbers (filled on create).",
            "items": {
              "$ref": "#/components/schemas/OrdersAttachedPhone"
            }
          }
        }
      },
      "OrdersOrderJSON": {
        "type": "object",
        "description": "Snapshot of the order content and prices.",
        "properties": {
          "total": {
            "type": "number",
            "description": "Amount to pay = goodsTotal + refill (UAH rounded up to integer).",
            "example": 6.49
          },
          "goodsTotal": {
            "type": "number",
            "description": "Price of goods only.",
            "example": 5.99
          },
          "refill": {
            "type": "number",
            "description": "Amount credited to the balance (top-up + starter fee).",
            "example": 0.5
          },
          "starterFee": {
            "type": "number",
            "description": "Starter fee added when a number is bought and the balance would fall below the threshold (0.5 USD / 20 UAH).",
            "example": 0.5
          },
          "phones": {
            "type": "array",
            "description": "Number lines.",
            "items": {
              "$ref": "#/components/schemas/OrdersPhoneLine"
            }
          },
          "tariff": {
            "type": [
              "object",
              "null"
            ],
            "description": "Tariff snapshot `{ id, Name, calculatedMonthPrice, prices }` or null.",
            "additionalProperties": true,
            "example": null
          },
          "esim": {
            "type": [
              "object",
              "null"
            ],
            "description": "eSIM snapshot `{ id, name, price, recalculatedEsimPrice, usage_amount, days_amount, country_iso_code, region_name, iccid, isInfinity, provider }` or null.",
            "additionalProperties": true,
            "example": null
          },
          "domain": {
            "type": "array",
            "description": "Extra domains `{ name, value: { id, name }, calculatedMonthPrice }`.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "Domain line."
            }
          },
          "recall_widget": {
            "type": "array",
            "description": "Extra recall widgets.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "Widget line."
            }
          },
          "callback_widget": {
            "type": "array",
            "description": "Extra callback widgets.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "Widget line."
            }
          },
          "sip-trunk": {
            "type": "array",
            "description": "SIP trunks with `calculatedMonthPrice`.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "Trunk line."
            }
          },
          "promo": {
            "type": [
              "object",
              "null"
            ],
            "description": "Applied promo code `{ id, promoCode }` or null.",
            "additionalProperties": true,
            "example": null
          },
          "payment": {
            "type": [
              "object",
              "null"
            ],
            "description": "Payment link and result: `url` (Stripe) / `invoiceUrl` (WayForPay) / `processUrl` (balance); `paymentType: intent` for mobile; `message` with the decline reason (e.g. `Not enough money on balance`). Null in the price calculation.",
            "additionalProperties": true,
            "example": {
              "url": "https://api.onevoiplanet.com/api/orders/process-payment/1790923456789123"
            }
          },
          "incomingOrderId": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Internal link to a parent order; usually null.",
            "example": null
          },
          "user_ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "IP of the client that created the order.",
            "example": "203.0.113.10"
          }
        }
      },
      "OrdersOrderAttributes": {
        "type": "object",
        "description": "Order attributes.",
        "properties": {
          "orderId": {
            "type": "string",
            "description": "Order number (string). Used in payment links and `POST /payment-system/stripe/payment-sheet`.",
            "example": "1788231620358546"
          },
          "orderStatus": {
            "type": "string",
            "enum": [
              "new",
              "pending",
              "frozen",
              "approved",
              "declined",
              "refunded"
            ],
            "description": "`new` — created, waiting for payment; `pending` — payment/provider in progress; `frozen` — waiting for identity verification; `approved` — paid and processed; `declined` — rejected/expired/not enough balance; `refunded` — refunded.",
            "example": "approved"
          },
          "orderType": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ],
            "description": "`incoming` — balance top-up; `outgoing` — purchase.",
            "example": "outgoing"
          },
          "amount": {
            "type": "number",
            "description": "Order total (= `orderJSON.total`).",
            "example": 6.49
          },
          "auto_renewal": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Auto-renewal requested for the created subscriptions.",
            "example": true
          },
          "redirect_link": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "global",
              "mobile"
            ],
            "description": "Where the gateway returns the user: web cabinet or mobile app (detected by User-Agent).",
            "example": "global"
          },
          "source": {
            "type": "string",
            "enum": [
              "default",
              "tarification"
            ],
            "description": "`default` — created by the client; `tarification` — created by the system (renewals).",
            "example": "default"
          },
          "template_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Notification template used for the status email.",
            "example": null
          },
          "additional_data": {
            "type": [
              "object",
              "null"
            ],
            "description": "Processing details, e.g. `didww_pending.items[]` with `done`/`failed` flags for numbers bought in the background.",
            "additionalProperties": true,
            "example": null
          },
          "orderJSON": {
            "$ref": "#/components/schemas/OrdersOrderJSON"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-09-01T03:00:20.402Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-09-01T03:00:25.402Z"
          },
          "payment_system": {
            "type": "object",
            "description": "Payment system (with populate).",
            "properties": {
              "data": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Relation data.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "Id.",
                    "example": 3
                  },
                  "attributes": {
                    "type": "object",
                    "description": "Attributes.",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Display name.",
                        "example": "Stripe"
                      }
                    }
                  }
                }
              }
            }
          },
          "currency": {
            "type": "object",
            "description": "Currency (with populate).",
            "properties": {
              "data": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Relation data.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "Id.",
                    "example": 1
                  },
                  "attributes": {
                    "type": "object",
                    "description": "Attributes.",
                    "properties": {
                      "name_short": {
                        "type": "string",
                        "description": "ISO code.",
                        "example": "USD"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "OrdersOrder": {
        "type": "object",
        "description": "Order (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Order id.",
            "example": 42
          },
          "attributes": {
            "$ref": "#/components/schemas/OrdersOrderAttributes"
          }
        }
      },
      "OrdersOrderFlat": {
        "type": "object",
        "description": "Order as a flat object (returned by `POST /orders`).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Order id (use in `GET /orders/{id}`).",
            "example": 43
          },
          "orderId": {
            "type": "string",
            "description": "Order number.",
            "example": "1790923456789123"
          },
          "orderStatus": {
            "type": "string",
            "enum": [
              "new",
              "pending",
              "frozen",
              "approved",
              "declined",
              "refunded"
            ],
            "description": "Always `new` right after creation.",
            "example": "new"
          },
          "orderType": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ],
            "description": "Top-up or purchase.",
            "example": "outgoing"
          },
          "amount": {
            "type": "number",
            "description": "Order total.",
            "example": 6.49
          },
          "auto_renewal": {
            "type": "boolean",
            "description": "Auto-renewal flag.",
            "example": true
          },
          "redirect_link": {
            "type": "string",
            "enum": [
              "global",
              "mobile"
            ],
            "description": "Return target after gateway payment.",
            "example": "global"
          },
          "source": {
            "type": "string",
            "description": "Always `default` for client orders.",
            "example": "default"
          },
          "template_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Notification template.",
            "example": null
          },
          "additional_data": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Processing details.",
            "example": null
          },
          "orderJSON": {
            "$ref": "#/components/schemas/OrdersOrderJSON"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-10-02T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-10-02T10:00:00.000Z"
          }
        }
      },
      "SipCountryPrefix": {
        "type": "object",
        "description": "Country code of a number.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Id.",
            "example": 1
          },
          "country": {
            "type": "string",
            "description": "Country name/code.",
            "example": "UA"
          },
          "prefix": {
            "type": "string",
            "description": "Country calling code.",
            "example": "380"
          },
          "iso": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2.",
            "example": "UA"
          }
        }
      },
      "SipPhonePrefix": {
        "type": "object",
        "description": "City/operator prefix of a number.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Id.",
            "example": 9
          },
          "prefix": {
            "type": "string",
            "description": "Prefix after the country code.",
            "example": "44"
          }
        }
      },
      "SipPhoneAttributes": {
        "type": "object",
        "description": "Phone number.",
        "properties": {
          "number": {
            "type": "string",
            "description": "Number without country code and prefix.",
            "example": "1234567"
          },
          "fullnumber": {
            "type": "string",
            "description": "Full number in international format without `+`.",
            "example": "380441234567"
          },
          "channels": {
            "type": "integer",
            "description": "Simultaneous call channels.",
            "example": 4
          },
          "type": {
            "type": "string",
            "description": "Number type (simple, golden, exclusive…).",
            "example": "simple"
          },
          "reserved": {
            "type": "boolean",
            "description": "Number is reserved (not yet active).",
            "example": false
          },
          "priority": {
            "type": "integer",
            "description": "Priority within the catalogue.",
            "example": 0
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-02-01T09:30:00.000Z"
          },
          "country_prefix": {
            "$ref": "#/components/schemas/SipCountryPrefix"
          },
          "phone_prefix": {
            "$ref": "#/components/schemas/SipPhonePrefix"
          }
        }
      },
      "SipPhoneLine": {
        "type": "object",
        "description": "Phone number (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Phone id.",
            "example": 25
          },
          "attributes": {
            "$ref": "#/components/schemas/SipPhoneAttributes"
          }
        }
      },
      "SipPhoneFlat": {
        "allOf": [
          {
            "type": "object",
            "description": "Phone number (flat).",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Phone id.",
                "example": 25
              }
            }
          },
          {
            "$ref": "#/components/schemas/SipPhoneAttributes"
          }
        ]
      },
      "SipDomainFlat": {
        "type": "object",
        "description": "SIP domain (flat).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Domain id.",
            "example": 5
          },
          "name": {
            "type": "string",
            "description": "Domain name; SIP realm is `<name>.sip.onevoiplanet.com`.",
            "example": "example"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-01-15T10:00:00.000Z"
          }
        }
      },
      "SipLineFields": {
        "type": "object",
        "description": "SIP line settings.",
        "properties": {
          "number": {
            "type": "string",
            "maxLength": 5,
            "description": "Internal extension number (3–5 digits), the user part of the SIP login.",
            "example": "001"
          },
          "password": {
            "type": "string",
            "description": "SIP password (shown as `********` in docs). Treat as a secret.",
            "example": "********"
          },
          "record_call": {
            "type": "boolean",
            "description": "Calls of this line are recorded.",
            "example": true
          },
          "call_forwarding": {
            "type": "string",
            "enum": [
              "off",
              "on",
              "no_answer"
            ],
            "description": "Call forwarding mode.",
            "example": "off"
          },
          "redirectActivity": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Redirect of all calls to `redirectTo` is enabled.",
            "example": false
          },
          "redirectTo": {
            "type": [
              "string",
              "null"
            ],
            "description": "External number for redirect.",
            "example": null
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Created at.",
            "example": "2026-01-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Updated at.",
            "example": "2026-02-01T09:30:00.000Z"
          }
        }
      },
      "SipLine": {
        "type": "object",
        "description": "SIP line (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "SIP line id.",
            "example": 12
          },
          "attributes": {
            "description": "SIP line attributes.",
            "allOf": [
              {
                "$ref": "#/components/schemas/SipLineFields"
              },
              {
                "type": "object",
                "description": "Populated relations.",
                "properties": {
                  "domain": {
                    "type": "object",
                    "description": "SIP domain (with `populate[domain]`).",
                    "properties": {
                      "data": {
                        "$ref": "#/components/schemas/SipDomain"
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "SipLineFlat": {
        "allOf": [
          {
            "type": "object",
            "description": "SIP line (flat object).",
            "properties": {
              "id": {
                "type": "integer",
                "description": "SIP line id.",
                "example": 12
              }
            }
          },
          {
            "$ref": "#/components/schemas/SipLineFields"
          }
        ]
      },
      "SipMyLine": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SipLineFlat"
          },
          {
            "type": "object",
            "description": "Relations of my SIP line.",
            "properties": {
              "user": {
                "type": "object",
                "description": "Owner of the line and his group membership.",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "User id.",
                    "example": 7
                  },
                  "group_employee": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Group membership (null if none).",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Membership id.",
                        "example": 3
                      },
                      "group": {
                        "type": "object",
                        "description": "Call group.",
                        "properties": {
                          "id": {
                            "type": "integer",
                            "description": "Group id.",
                            "example": 2
                          },
                          "name": {
                            "type": "string",
                            "description": "Group name.",
                            "example": "Sales"
                          },
                          "in_phones": {
                            "type": "array",
                            "description": "Numbers ringing on the group.",
                            "items": {
                              "$ref": "#/components/schemas/SipPhoneFlat"
                            }
                          },
                          "out_phone": {
                            "oneOf": [
                              {
                                "$ref": "#/components/schemas/SipPhoneFlat"
                              },
                              {
                                "type": "null"
                              }
                            ],
                            "description": "Outgoing number of the group."
                          }
                        }
                      }
                    }
                  }
                }
              },
              "inPhones": {
                "type": "array",
                "description": "Company numbers that ring directly on this line.",
                "items": {
                  "$ref": "#/components/schemas/SipPhoneFlat"
                }
              },
              "outPhone": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/SipPhoneFlat"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Default outgoing number of the line."
              },
              "domain": {
                "$ref": "#/components/schemas/SipDomainFlat"
              }
            }
          }
        ]
      },
      "SipTrunkConnectionInput": {
        "type": "object",
        "description": "Connection component. `by-credentials`: login, password, host, port. `by-ip`: ip.",
        "required": [
          "__component"
        ],
        "properties": {
          "__component": {
            "type": "string",
            "enum": [
              "trunk-connection-type.by-credentials",
              "trunk-connection-type.by-ip"
            ],
            "description": "Component type.",
            "example": "trunk-connection-type.by-credentials"
          },
          "login": {
            "type": "string",
            "minLength": 3,
            "description": "SIP login at the carrier (by-credentials; no Cyrillic).",
            "example": "user123"
          },
          "password": {
            "type": "string",
            "minLength": 3,
            "description": "SIP password (by-credentials). Never returned in responses.",
            "example": "s3cret"
          },
          "host": {
            "type": "string",
            "minLength": 3,
            "description": "Carrier SIP host (by-credentials).",
            "example": "sip.carrier.example"
          },
          "port": {
            "type": "string",
            "maxLength": 6,
            "default": "5060",
            "description": "SIP port (by-credentials).",
            "example": "5060"
          },
          "ip": {
            "type": "string",
            "minLength": 3,
            "description": "IP address of your PBX (by-ip).",
            "example": "203.0.113.10"
          }
        }
      },
      "SipTrunk": {
        "type": "object",
        "description": "SIP trunk.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Trunk id.",
            "example": 3
          },
          "attributes": {
            "type": "object",
            "description": "Trunk attributes.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Trunk name.",
                "example": "Carrier trunk"
              },
              "registration": {
                "type": "boolean",
                "description": "In list: live registration state at the carrier; in other methods: the stored registration flag.",
                "example": true
              },
              "connection_credentials": {
                "type": "array",
                "description": "Connection settings (password is not returned).",
                "items": {
                  "type": "object",
                  "description": "Connection component.",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "description": "Component id.",
                      "example": 1
                    },
                    "__component": {
                      "type": "string",
                      "description": "Component type.",
                      "example": "trunk-connection-type.by-credentials"
                    },
                    "login": {
                      "type": "string",
                      "description": "SIP login (by-credentials).",
                      "example": "user123"
                    },
                    "host": {
                      "type": "string",
                      "description": "SIP host (by-credentials).",
                      "example": "sip.carrier.example"
                    },
                    "port": {
                      "type": "string",
                      "description": "SIP port (by-credentials).",
                      "example": "5060"
                    },
                    "ip": {
                      "type": "string",
                      "description": "PBX IP (by-ip).",
                      "example": "203.0.113.10"
                    }
                  }
                }
              },
              "phones": {
                "type": "array",
                "description": "External numbers of the trunk (list method, or `populate[phones]`).",
                "items": {
                  "type": "object",
                  "description": "External number.",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "description": "Phone id.",
                      "example": 44
                    },
                    "fullnumber": {
                      "type": "string",
                      "description": "Full number without `+`.",
                      "example": "380441234567"
                    }
                  }
                }
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-02-01T09:30:00.000Z"
              }
            }
          }
        }
      },
      "SipDomain": {
        "type": "object",
        "description": "SIP domain (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Domain id.",
            "example": 5
          },
          "attributes": {
            "type": "object",
            "description": "Domain attributes.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Domain name; SIP realm is `<name>.sip.onevoiplanet.com`.",
                "example": "example"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-01-15T10:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-01-15T10:00:00.000Z"
              }
            }
          }
        }
      },
      "SmsChat": {
        "type": "object",
        "description": "SMS chat: dialogue between one company number and one external number.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Chat id.",
            "example": 12
          },
          "attributes": {
            "type": "object",
            "description": "Chat attributes.",
            "properties": {
              "dialog_with": {
                "type": "string",
                "description": "External party number, E.164 digits without `+`.",
                "example": "380671234567"
              },
              "last_message": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Text of the last message.",
                "example": "Thank you, see you tomorrow"
              },
              "last_message_time": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Time of the last message (ISO-8601, UTC).",
                "example": "2026-03-10T09:15:00.000Z"
              },
              "last_message_receiver": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Receiver of the last message (digits without `+`): the external number for outgoing, the company number for incoming.",
                "example": "380671234567"
              },
              "last_message_type": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "incoming",
                  "outgoing",
                  null
                ],
                "description": "Direction of the last message.",
                "example": "outgoing"
              },
              "seen": {
                "type": "boolean",
                "description": "Whether all messages of the chat are read.",
                "example": true
              },
              "alpha_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Display name of the external party: taken from the company address book (contact name) or from the sender alpha-name; null if unknown.",
                "example": "John Doe"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at (ISO-8601, UTC).",
                "example": "2026-03-01T08:00:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at (ISO-8601, UTC).",
                "example": "2026-03-10T09:15:01.000Z"
              },
              "messages": {
                "type": "object",
                "description": "Messages of the chat (only with `populate[messages]`).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Messages.",
                    "items": {
                      "$ref": "#/components/schemas/SmsMessage"
                    }
                  }
                }
              },
              "receiver_phone_relation": {
                "type": "object",
                "description": "Company number of the dialogue (only with `populate[receiver_phone_relation]`).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Phone object (`id`, `attributes.fullnumber`, …).",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Phone id.",
                        "example": 7
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Phone attributes.",
                        "properties": {
                          "fullnumber": {
                            "type": "string",
                            "description": "Company number, digits without `+`.",
                            "example": "380441234567"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SmsMessage": {
        "type": "object",
        "description": "SMS message.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Message id.",
            "example": 101
          },
          "attributes": {
            "type": "object",
            "description": "Message attributes.",
            "properties": {
              "sender": {
                "type": "string",
                "description": "Sender number, digits without `+` (company number for outgoing, external number for incoming).",
                "example": "380441234567"
              },
              "receiver": {
                "type": "string",
                "description": "Receiver number, digits without `+`.",
                "example": "380671234567"
              },
              "receive_time": {
                "type": "string",
                "format": "date-time",
                "description": "Time the message was received by the platform / accepted for sending (ISO-8601, UTC).",
                "example": "2026-03-10T09:15:00.000Z"
              },
              "type": {
                "type": "string",
                "enum": [
                  "incoming",
                  "outgoing"
                ],
                "description": "Direction.",
                "example": "outgoing"
              },
              "status": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "queued",
                  "success",
                  "error",
                  null
                ],
                "description": "Delivery status. Outgoing: `queued` — accepted and handed to the provider, `success` — delivered to the operator, `error` — not sent (insufficient balance, no price, provider error). Incoming messages are always `success`.",
                "example": "success"
              },
              "seen": {
                "type": "boolean",
                "description": "Read flag. Outgoing messages are created with `true`, incoming with `false`.",
                "example": true
              },
              "message": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Message text.",
                "example": "Thank you, see you tomorrow"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at (ISO-8601, UTC).",
                "example": "2026-03-10T09:15:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at (ISO-8601, UTC); changes when the delivery status changes.",
                "example": "2026-03-10T09:15:03.000Z"
              },
              "chat": {
                "type": "object",
                "description": "Chat the message belongs to (only with `populate[chat]`).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Chat (`id`, `attributes`).",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Chat id.",
                        "example": 12
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Chat attributes (see SmsChat).",
                        "properties": {
                          "dialog_with": {
                            "type": "string",
                            "description": "External party number.",
                            "example": "380671234567"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "receiver_phone_relation": {
                "type": "object",
                "description": "Company number used in the message (only with `populate[receiver_phone_relation]`).",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Phone object.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Phone id.",
                        "example": 7
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Phone attributes.",
                        "properties": {
                          "fullnumber": {
                            "type": "string",
                            "description": "Company number, digits without `+`.",
                            "example": "380441234567"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SmsSentMessage": {
        "type": "object",
        "description": "Outgoing message as returned right after `POST /sms/outgoing/send`.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Message id — use it to poll the delivery status.",
            "example": 101
          },
          "attributes": {
            "type": "object",
            "description": "Message attributes.",
            "properties": {
              "sender": {
                "type": "string",
                "description": "Company number the SMS is sent from, digits without `+`.",
                "example": "380441234567"
              },
              "receiver": {
                "type": "string",
                "description": "Normalised destination number, E.164 digits without `+`.",
                "example": "380671234567"
              },
              "receive_time": {
                "type": "string",
                "format": "date-time",
                "description": "Time accepted for sending (ISO-8601, UTC).",
                "example": "2026-03-10T09:15:00.000Z"
              },
              "type": {
                "type": "string",
                "enum": [
                  "outgoing"
                ],
                "description": "Always `outgoing`.",
                "example": "outgoing"
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued",
                  "error"
                ],
                "description": "`queued` — accepted and charged, delivery in progress; `error` — rejected without charge (insufficient balance or no price configured).",
                "example": "queued"
              },
              "seen": {
                "type": "boolean",
                "description": "Always `true` for outgoing messages.",
                "example": true
              },
              "message": {
                "type": "string",
                "description": "Message text as sent.",
                "example": "Thank you, see you tomorrow"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at (ISO-8601, UTC).",
                "example": "2026-03-10T09:15:00.000Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at (ISO-8601, UTC).",
                "example": "2026-03-10T09:15:00.000Z"
              },
              "company": {
                "type": "object",
                "description": "Company reference (only `id` and the currency reference are returned).",
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Company.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Company id.",
                        "example": 1
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Company attributes (currency reference only).",
                        "properties": {
                          "currency": {
                            "type": "object",
                            "description": "Currency the charge was made in.",
                            "properties": {
                              "data": {
                                "type": "object",
                                "description": "Currency (`id` only).",
                                "properties": {
                                  "id": {
                                    "type": "integer",
                                    "description": "Currency id.",
                                    "example": 1
                                  },
                                  "attributes": {
                                    "type": "object",
                                    "description": "Currency attributes — always an empty object here.",
                                    "example": {}
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SmsPhone": {
        "type": "object",
        "description": "Company number with SMS support and the SMS prices of its range.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Phone id — pass it as `phone` in `POST /sms/outgoing/send`.",
            "example": 7
          },
          "attributes": {
            "type": "object",
            "description": "Phone attributes.",
            "properties": {
              "fullnumber": {
                "type": "string",
                "description": "Number, digits without `+`.",
                "example": "380441234567"
              },
              "country_prefix": {
                "type": "object",
                "description": "Country of the number.",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Country.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Country id.",
                        "example": 3
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Country attributes.",
                        "properties": {
                          "iso": {
                            "type": "string",
                            "description": "ISO 3166-1 alpha-2 code.",
                            "example": "UA"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "phone_prefix": {
                "type": "object",
                "description": "Number range (prefix) with its SMS capabilities and prices.",
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Range.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Range id.",
                        "example": 15
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Range attributes.",
                        "properties": {
                          "outgoing_sms": {
                            "type": "boolean",
                            "description": "Outgoing SMS supported (always `true` in this list).",
                            "example": true
                          },
                          "prices": {
                            "type": "object",
                            "description": "Price lists of the range (one per currency; the currency itself is not returned).",
                            "properties": {
                              "data": {
                                "type": "array",
                                "description": "Price lists.",
                                "items": {
                                  "type": "object",
                                  "description": "Price list.",
                                  "properties": {
                                    "id": {
                                      "type": "integer",
                                      "description": "Price list id.",
                                      "example": 40
                                    },
                                    "attributes": {
                                      "type": "object",
                                      "description": "Price list attributes.",
                                      "properties": {
                                        "price_by_field": {
                                          "type": "array",
                                          "description": "Named prices; look for `sms_outgoing_price` and `sms_incoming_price` (price per SMS segment).",
                                          "items": {
                                            "type": "object",
                                            "description": "Named price.",
                                            "properties": {
                                              "id": {
                                                "type": "integer",
                                                "description": "Item id.",
                                                "example": 901
                                              },
                                              "name": {
                                                "type": "string",
                                                "description": "Price name (`sms_outgoing_price`, `sms_incoming_price`, `price`, `setup_price`, …).",
                                                "example": "sms_outgoing_price"
                                              },
                                              "price": {
                                                "type": "number",
                                                "description": "Price value.",
                                                "example": 0.05
                                              }
                                            }
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SmsSendRequest": {
        "type": "object",
        "description": "Body of `POST /sms/outgoing/send`.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "description": "Message to send.",
            "required": [
              "phone",
              "destination",
              "message"
            ],
            "properties": {
              "phone": {
                "type": "integer",
                "minimum": 1,
                "description": "Id of the company number to send from (from `GET /sms/phones`). Must belong to your company and support outgoing SMS.",
                "example": 7
              },
              "destination": {
                "type": "string",
                "minLength": 3,
                "description": "Destination number: national format of the sender's country or international digits without `+` (all non-digits are stripped). Numbers in the United States are rejected.",
                "example": "380671234567"
              },
              "message": {
                "type": "string",
                "minLength": 1,
                "description": "Message text. Charged per 160-character segment (`ceil(length/160)`).",
                "example": "Thank you, see you tomorrow"
              }
            }
          }
        }
      },
      "SmsMarkSeenRequest": {
        "type": "object",
        "description": "Body of `PUT /sms/update-messages-status`.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "description": "Messages to mark as read.",
            "required": [
              "chat",
              "ids"
            ],
            "properties": {
              "chat": {
                "type": "integer",
                "description": "Chat id the messages belong to.",
                "example": 12
              },
              "ids": {
                "type": "array",
                "description": "Ids of messages to mark as read.",
                "items": {
                  "type": "integer",
                  "description": "Message id.",
                  "example": 100
                },
                "example": [
                  100,
                  101
                ]
              }
            }
          }
        }
      },
      "SmsMessageSeenState": {
        "type": "object",
        "description": "Read state of a message and its chat.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Message id.",
            "example": 100
          },
          "attributes": {
            "type": "object",
            "description": "State.",
            "properties": {
              "seen": {
                "type": "boolean",
                "description": "Always `true` after the call.",
                "example": true
              },
              "chat": {
                "type": "object",
                "description": "Chat state.",
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Chat.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Chat id.",
                        "example": 12
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Chat attributes.",
                        "properties": {
                          "seen": {
                            "type": "boolean",
                            "description": "Whether the chat is now read.",
                            "example": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SubscriptionServiceComponent": {
        "type": "object",
        "description": "What the subscription pays for (dynamic zone item).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Component id.",
            "example": 385
          },
          "__component": {
            "type": "string",
            "enum": [
              "subscription-service.phones",
              "subscription-service.external-phone",
              "subscription-service.tariff",
              "subscription-service.domain",
              "subscription-service.sip-trunk",
              "subscription-service.recall-widget",
              "subscription-service.callback-widget"
            ],
            "description": "Service type.",
            "example": "subscription-service.phones"
          },
          "phones": {
            "type": "object",
            "additionalProperties": true,
            "description": "Number relation (for `phones`/`external-phone`, with populate): `{ data: { id, attributes: { fullnumber } } }`."
          },
          "tariff": {
            "type": "object",
            "additionalProperties": true,
            "description": "Tariff relation (with populate)."
          },
          "domain": {
            "type": "object",
            "additionalProperties": true,
            "description": "Domain relation (with populate)."
          },
          "sip_trunk": {
            "type": "object",
            "additionalProperties": true,
            "description": "SIP trunk relation (with populate)."
          },
          "recall_widget": {
            "type": "object",
            "additionalProperties": true,
            "description": "Recall widget relation (with populate)."
          },
          "callback_widget": {
            "type": "object",
            "additionalProperties": true,
            "description": "Callback widget relation (with populate)."
          }
        }
      },
      "Subscription": {
        "type": "object",
        "description": "Subscription (Strapi envelope).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Subscription id.",
            "example": 12
          },
          "attributes": {
            "type": "object",
            "description": "Attributes.",
            "properties": {
              "next_billing_date": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date",
                "description": "Date of the next monthly charge (the paid period ends here).",
                "example": "2026-11-01"
              },
              "amount": {
                "type": "number",
                "description": "Monthly amount in the company currency. 0 — the number prefix price is used.",
                "example": 5.99
              },
              "payeed": {
                "type": "boolean",
                "description": "Current period is paid. false — the service is suspended; restore within 30 days.",
                "example": true
              },
              "active": {
                "type": "boolean",
                "description": "Auto-renewal enabled.",
                "example": true
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "description": "Created at.",
                "example": "2026-04-22T07:08:01.714Z"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "Updated at.",
                "example": "2026-10-01T03:00:04.393Z"
              },
              "service_name": {
                "type": "array",
                "description": "Services covered by the subscription (usually one).",
                "items": {
                  "$ref": "#/components/schemas/SubscriptionServiceComponent"
                }
              },
              "payments": {
                "type": "object",
                "description": "Charge history (with populate).",
                "properties": {
                  "data": {
                    "type": "array",
                    "description": "Payments.",
                    "items": {
                      "type": "object",
                      "description": "Payment.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Payment id.",
                          "example": 101
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Attributes.",
                          "properties": {
                            "status": {
                              "type": "string",
                              "enum": [
                                "new",
                                "pending",
                                "approved",
                                "declined",
                                "refunded"
                              ],
                              "description": "Status.",
                              "example": "approved"
                            },
                            "amount": {
                              "type": "number",
                              "description": "Negative — charge.",
                              "example": -5.99
                            },
                            "commentary": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Comment.",
                              "example": "Payment: 191703"
                            },
                            "createdAt": {
                              "type": "string",
                              "format": "date-time",
                              "description": "Created at.",
                              "example": "2026-10-01T03:00:04.488Z"
                            },
                            "updatedAt": {
                              "type": "string",
                              "format": "date-time",
                              "description": "Updated at.",
                              "example": "2026-10-01T03:00:04.911Z"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "payment_system": {
                "type": "object",
                "description": "Card gateway used as fallback when the balance is insufficient (with populate); `data: null` — balance only.",
                "properties": {
                  "data": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Relation data.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Id.",
                        "example": 3
                      },
                      "attributes": {
                        "type": "object",
                        "description": "Attributes.",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "Display name.",
                            "example": "Stripe"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SubscriptionRestoreResult": {
        "type": "object",
        "description": "Restore result.",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "true — the charge was started (or is already in flight).",
            "example": true
          },
          "status": {
            "type": "string",
            "enum": [
              "processing"
            ],
            "description": "`processing` — wait for `payeed: true`.",
            "example": "processing"
          },
          "subscriptionId": {
            "type": "integer",
            "description": "Subscription id.",
            "example": 12
          },
          "amount": {
            "type": "number",
            "description": "Amount charged (or required).",
            "example": 5.99
          },
          "reason": {
            "type": "string",
            "enum": [
              "already_active",
              "non_restorable",
              "insufficient_balance"
            ],
            "description": "Why not restored: already paid and not overdue; more than 30 days overdue; not enough balance.",
            "example": "insufficient_balance"
          },
          "balance": {
            "type": "number",
            "description": "Current balance (with `insufficient_balance`).",
            "example": 1.2
          }
        }
      },
      "SubscriptionPhoneRow": {
        "type": "object",
        "description": "Number with subscription state.",
        "properties": {
          "phone_id": {
            "type": "integer",
            "description": "Phone id.",
            "example": 25
          },
          "fullnumber": {
            "type": "string",
            "description": "Full number without `+`.",
            "example": "380441234567"
          },
          "number": {
            "type": "string",
            "description": "Local part of the number.",
            "example": "1234567"
          },
          "custom": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "External (own) number.",
            "example": null
          },
          "verification_required": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Regulator identity verification is required for this number.",
            "example": null
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the number was verified.",
            "example": null
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Number created at.",
            "example": "2026-04-07T12:25:18.282Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Number updated at.",
            "example": "2026-10-01T03:00:04.393Z"
          },
          "company_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Your company id; null for detached numbers.",
            "example": 7
          },
          "subscription_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Subscription id (for restore / auto-renewal calls); null if none.",
            "example": 12
          },
          "active": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Auto-renewal enabled.",
            "example": true
          },
          "payeed": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Current period paid.",
            "example": true
          },
          "next_billing_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Next charge date.",
            "example": "2026-11-01"
          },
          "subscription_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Subscription amount (0 — prefix price applies).",
            "example": 5.99
          },
          "prefix_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Prefix id.",
            "example": 9
          },
          "prefix": {
            "type": [
              "string",
              "null"
            ],
            "description": "Prefix digits.",
            "example": "44"
          },
          "owner_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "City / network name.",
            "example": "Kyiv"
          },
          "owner_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Prefix type: city, mobile, toll-free, ...",
            "example": "city"
          },
          "country_iso": {
            "type": [
              "string",
              "null"
            ],
            "description": "Country ISO code.",
            "example": "UA"
          },
          "prefix_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Monthly price of the prefix in the company currency.",
            "example": 5.99
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expired"
            ],
            "description": "`active` — paid and within the period; `expired` — unpaid or period passed.",
            "example": "active"
          },
          "recovery_status": {
            "type": "string",
            "enum": [
              "not_applicable",
              "restorable",
              "non_restorable",
              "unknown"
            ],
            "description": "For expired numbers: `restorable` — can be restored (within 30 days), `non_restorable` — released, `unknown` — no billing anchor.",
            "example": "not_applicable"
          },
          "verification": {
            "type": "object",
            "description": "Regulator verification state.",
            "properties": {
              "required": {
                "type": "boolean",
                "description": "Verification required.",
                "example": false
              },
              "status": {
                "type": "string",
                "enum": [
                  "none",
                  "required",
                  "pending",
                  "approved",
                  "rejected"
                ],
                "description": "`none` — not needed; `required` — needed, no request yet; `pending`/`rejected` — by the last request; `approved` — verified.",
                "example": "none"
              },
              "verification_id": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Last verification request id.",
                "example": null
              },
              "reject_reasons": {
                "type": [
                  "array",
                  "null"
                ],
                "description": "Reject reasons of the last request.",
                "items": {
                  "type": "string",
                  "description": "Reason."
                },
                "example": null
              },
              "reject_comment": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Reject comment.",
                "example": null
              },
              "verified_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Verified at.",
                "example": null
              }
            }
          },
          "lifecycle_anchor_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Date the lifecycle is counted from (billing date or purchase + 30 days).",
            "example": "2026-11-01"
          },
          "lifecycle_days_after_anchor": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Days since the anchor (negative — still in the paid period).",
            "example": -30
          },
          "is_detached": {
            "type": "boolean",
            "description": "Number is no longer attached to the company (history row).",
            "example": false
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error or malformed request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "data": null,
              "error": {
                "status": 400,
                "name": "ValidationError",
                "message": "email must be a valid email",
                "details": {
                  "errors": [
                    {
                      "path": [
                        "email"
                      ],
                      "message": "email must be a valid email",
                      "name": "ValidationError"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "data": null,
              "error": {
                "status": 401,
                "name": "UnauthorizedError",
                "message": "Missing or invalid credentials",
                "details": {}
              }
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Not enough money on the balance to perform the operation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "data": null,
              "error": {
                "status": 402,
                "name": "PaymentRequiredError",
                "message": "Not enough money on balance",
                "details": {}
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The role has no permission for this method, or the object belongs to another company.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "data": null,
              "error": {
                "status": 403,
                "name": "ForbiddenError",
                "message": "Forbidden",
                "details": {}
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Object not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "data": null,
              "error": {
                "status": 404,
                "name": "NotFoundError",
                "message": "Not Found",
                "details": {}
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded. Retry after the `Retry-After` header value (seconds).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "data": null,
              "error": {
                "status": 429,
                "name": "RateLimitError",
                "message": "Too many requests, please try again later.",
                "details": {}
              }
            }
          }
        }
      },
      "ServerError": {
        "description": "Internal error. Retry later; if it repeats, contact support with the request time and path.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "data": null,
              "error": {
                "status": 500,
                "name": "InternalServerError",
                "message": "Internal Server Error",
                "details": {}
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/companies/my": {
      "get": {
        "operationId": "companyGetMy",
        "tags": [
          "Account & Company"
        ],
        "summary": "My company (full profile)",
        "description": "Returns the company of the authenticated user with all main relations populated by default: tariff (with prices\nand restrictions), phone numbers (with their routing to SIP lines / groups / IVR), users, SIP domains, call groups,\nIVRs, callback/recall widgets, CRM integrations, notification settings, currency, payment system and SIP trunks.\nThe response is large; for a light summary use `GET /companies/getBasicInfo`.\n`filters`, `pagination` and `_q` query parameters are ignored; a custom `populate` is merged with the default one.\n\n\n**Required role:** Employee, Company owner (registered, not yet approved), Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Company with relations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CompanyFull"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 7,
                    "attributes": {
                      "Name": "Example LLC",
                      "YRGPO": "12345678",
                      "validation": "yes",
                      "tarificationDate": "2026-02-01",
                      "last_success_tarification": "2026-01-01",
                      "missed_calls_timing": {
                        "redTiming": 1800,
                        "greenTiming": 60,
                        "yellowTiming": 300
                      },
                      "demo_untill": null,
                      "credit_limit": 5,
                      "use_credit_for_service_payment": false,
                      "balance_modified_date": "2026-01-20T10:18:11.057Z",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T06:32:20.297Z",
                      "tariff": {
                        "data": {
                          "id": 2,
                          "attributes": {
                            "Name": "Pro",
                            "custom": false,
                            "prices": {
                              "data": [
                                {
                                  "id": 12,
                                  "attributes": {
                                    "currency": {
                                      "data": {
                                        "id": 3,
                                        "attributes": {
                                          "name_short": "UAH",
                                          "name_long": "Hryvnia",
                                          "symbol": "₴"
                                        }
                                      }
                                    },
                                    "price_by_field": [
                                      {
                                        "id": 1,
                                        "name": "price",
                                        "price": 629,
                                        "isPriceStatic": false
                                      },
                                      {
                                        "id": 2,
                                        "name": "phone_channel_price",
                                        "price": 29,
                                        "isPriceStatic": false
                                      }
                                    ]
                                  }
                                }
                              ]
                            },
                            "tariff_restrictions": {
                              "data": [
                                {
                                  "id": 2,
                                  "attributes": {
                                    "maxPhones": 50,
                                    "maxEmployees": 20,
                                    "maxDomains": 3
                                  }
                                }
                              ]
                            }
                          }
                        }
                      },
                      "phones": {
                        "data": [
                          {
                            "id": 35,
                            "attributes": {
                              "number": "1234567",
                              "fullnumber": "380441234567",
                              "channels": 2,
                              "type": "exclusive",
                              "reserved": false,
                              "priority": 5,
                              "inSip": {
                                "data": {
                                  "id": 15,
                                  "attributes": {
                                    "number": "001"
                                  }
                                }
                              },
                              "in_group": {
                                "data": null
                              },
                              "in_ivr_line": {
                                "data": null
                              },
                              "country_prefix": {
                                "data": {
                                  "id": 1,
                                  "attributes": {
                                    "prefix": "380",
                                    "name": "Ukraine"
                                  }
                                }
                              }
                            }
                          }
                        ]
                      },
                      "register_ticket": {
                        "data": []
                      },
                      "users": {
                        "data": [
                          {
                            "id": 42,
                            "attributes": {
                              "username": "owner",
                              "email": "owner@example.com",
                              "firstName": "John",
                              "lastName": "Doe",
                              "confirmed": true,
                              "blocked": false
                            }
                          }
                        ]
                      },
                      "domains": {
                        "data": [
                          {
                            "id": 3,
                            "attributes": {
                              "name": "examplellc",
                              "payyed": true
                            }
                          }
                        ]
                      },
                      "groups": {
                        "data": [
                          {
                            "id": 9,
                            "attributes": {
                              "name": "Sales",
                              "number": "900"
                            }
                          }
                        ]
                      },
                      "recall_widgets": {
                        "data": []
                      },
                      "callback_widgets": {
                        "data": []
                      },
                      "ivrs": {
                        "data": []
                      },
                      "integration": [],
                      "notification_setting": {
                        "data": {
                          "id": 11,
                          "attributes": {
                            "send_notification_at": "09:00:00.000",
                            "minimum_balance": 100,
                            "notification_contacts": [
                              {
                                "id": 1,
                                "type": "email",
                                "contact": "owner@example.com",
                                "finance": true,
                                "call": true,
                                "widgets": true,
                                "statistics": true,
                                "lang": "en"
                              }
                            ]
                          }
                        }
                      },
                      "incoming_payment_system": {
                        "data": {
                          "id": 5,
                          "attributes": {
                            "name": "wayforpay",
                            "serviceName": "wayforpay"
                          }
                        }
                      },
                      "currency": {
                        "data": {
                          "id": 3,
                          "attributes": {
                            "name_short": "UAH",
                            "name_long": "Hryvnia",
                            "symbol": "₴",
                            "code": 980
                          }
                        }
                      },
                      "sip_trunks": {
                        "data": []
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/my\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/my\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/my\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/getBasicInfo": {
      "get": {
        "operationId": "companyGetBasicInfo",
        "tags": [
          "Account & Company"
        ],
        "summary": "Short account summary (user + company + tariff)",
        "description": "Light-weight summary for the app header: the user's name, role, company currency, tariff with prices in the\ncompany currency and the number-count limit, outgoing numbers of the user's SIP line, and the payment acquirer\nto show (`effective_payment_acquirer`: `wayforpay` when the client IP is in Ukraine, otherwise `stripe`).\nNote: `data.id` is the **user** id, not the company id.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CompanyBasicInfo"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 42,
                    "attributes": {
                      "firstName": "John",
                      "lastName": "Doe",
                      "middleName": null,
                      "email": "owner@example.com",
                      "username": "owner",
                      "company": {
                        "id": 7,
                        "currency": {
                          "id": 3,
                          "name_short": "UAH",
                          "symbol": "₴"
                        },
                        "tariff": {
                          "id": 2,
                          "Name": "Pro",
                          "prices": [
                            {
                              "id": 12,
                              "price_by_field": [
                                {
                                  "id": 1,
                                  "name": "price",
                                  "price": 629,
                                  "isPriceStatic": false
                                },
                                {
                                  "id": 2,
                                  "name": "phone_channel_price",
                                  "price": 29,
                                  "isPriceStatic": false
                                },
                                {
                                  "id": 3,
                                  "name": "domain_cost",
                                  "price": 119,
                                  "isPriceStatic": false
                                }
                              ]
                            }
                          ],
                          "tariff_restrictions": [
                            {
                              "id": 2,
                              "maxPhones": 50
                            }
                          ]
                        }
                      },
                      "sip": {
                        "id": 15,
                        "out_phone_lines": [
                          {
                            "id": 35,
                            "fullnumber": "380441234567"
                          }
                        ]
                      },
                      "role": {
                        "id": 4,
                        "name": "Company owner (approved)",
                        "description": "After company approved, tariff needed",
                        "type": "company_owner_approved"
                      },
                      "effective_payment_acquirer": "wayforpay",
                      "effective_country": "UA"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/getBasicInfo\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/getBasicInfo\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/getBasicInfo\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/hasAttachedSip": {
      "get": {
        "operationId": "companyHasAttachedSip",
        "tags": [
          "Account & Company"
        ],
        "summary": "Does the company have a number routed anywhere",
        "description": "Returns `sipAttached: true` if at least one phone number of the company is routed to a SIP line, a call group\nor an IVR (incoming or outgoing). Used by the apps to decide whether to show the \"set up routing\" onboarding step.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Flag.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Result (no id).",
                      "properties": {
                        "attributes": {
                          "type": "object",
                          "description": "Attributes.",
                          "properties": {
                            "sipAttached": {
                              "type": "boolean",
                              "description": "At least one number is routed.",
                              "example": true
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "attributes": {
                      "sipAttached": true
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/hasAttachedSip\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/hasAttachedSip\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/hasAttachedSip\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/roles": {
      "get": {
        "operationId": "companyGetRoles",
        "tags": [
          "Account & Company"
        ],
        "summary": "Roles assignable to employees",
        "description": "Returns the roles that can be given to employees (owner roles are excluded): `employee`, `Superwisor`, `Finance`.\nPass `name` as `role` in `POST /employees`, and `id` as `data.role` in `PUT /employees/{id}`.\nFlat array, no `data` envelope.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Roles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "description": "Role.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Role id.",
                        "example": 1
                      },
                      "name": {
                        "type": "string",
                        "description": "Role name (used in POST /employees).",
                        "example": "employee"
                      }
                    }
                  }
                },
                "example": [
                  {
                    "id": 1,
                    "name": "employee"
                  },
                  {
                    "id": 6,
                    "name": "Superwisor"
                  },
                  {
                    "id": 8,
                    "name": "Finance"
                  }
                ]
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/roles\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/roles\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/roles\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/getEmployees": {
      "get": {
        "operationId": "companyGetEmployees",
        "tags": [
          "Account & Company"
        ],
        "summary": "List employees of the company",
        "description": "Returns all users of the authenticated user's company (including the owner). Pagination parameters are **not\napplied** — the whole list is always returned; `meta.pagination.total` is the count. `sort`, `filters` and\n`populate` work (`populate[0]=role&populate[1]=sip&populate[2]=sip.domain` is the usual choice).\n\n\n**Required role:** Employee, Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Employees.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Users of the company.",
                      "items": {
                        "$ref": "#/components/schemas/CompanyUserEntity"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 42,
                      "attributes": {
                        "username": "owner",
                        "email": "owner@example.com",
                        "provider": "local",
                        "confirmed": true,
                        "blocked": false,
                        "firstName": "John",
                        "lastName": "Doe",
                        "middleName": null,
                        "type": false,
                        "birthDate": null,
                        "defaultLang": "en",
                        "registration_platform": "global",
                        "referral_uid": "f5436f2e0178004a599a96209c746f78",
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-02-01T12:00:00.000Z"
                      }
                    },
                    {
                      "id": 43,
                      "attributes": {
                        "username": "jane",
                        "email": "jane@example.com",
                        "provider": "local",
                        "confirmed": true,
                        "blocked": false,
                        "firstName": "Jane",
                        "lastName": "Doe",
                        "middleName": null,
                        "type": false,
                        "birthDate": null,
                        "defaultLang": "en",
                        "registration_platform": null,
                        "referral_uid": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
                        "createdAt": "2026-01-20T10:00:00.000Z",
                        "updatedAt": "2026-01-20T10:00:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 2,
                      "pageCount": 0,
                      "total": 2
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/getEmployees\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/getEmployees\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/getEmployees\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/employees": {
      "post": {
        "operationId": "companyAddEmployee",
        "tags": [
          "Account & Company"
        ],
        "summary": "Add an employee (creates a SIP line)",
        "description": "Creates a user in the company with the given role and a SIP line in the given SIP domain. The SIP number is the\nfirst free 3-digit number in the domain (001…999). The body is **flat** (no `data` envelope).\n`role` is the role **name** from `GET /companies/roles`. The number of employees is limited by the tariff\n(`maxEmployees`); when the company has no tariff the request is rejected with 403.\nThe created user can log in with `username`/`email` + `password`; the SIP client uses `sip.number@<domain>` + `sipPassword`.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor\n\n**Side effects:** Creates a user and a SIP line; sends an internal registration notification to the OneVOIPlanet team.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "username",
                  "password",
                  "role",
                  "domain"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "E-mail (unique).",
                    "example": "jane@example.com"
                  },
                  "username": {
                    "type": "string",
                    "description": "Login (unique).",
                    "example": "jane"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "description": "Password for the cabinet / apps.",
                    "example": "Secret123"
                  },
                  "role": {
                    "type": "string",
                    "description": "Role name: employee, Superwisor, Finance (see GET /companies/roles).",
                    "example": "employee"
                  },
                  "domain": {
                    "type": "integer",
                    "description": "SIP domain id of the company (GET /companies/my → domains).",
                    "example": 3
                  },
                  "sipPassword": {
                    "type": "string",
                    "format": "password",
                    "description": "Password of the SIP line.",
                    "example": "SipSecret789"
                  },
                  "firstName": {
                    "type": "string",
                    "description": "First name.",
                    "example": "Jane"
                  },
                  "lastName": {
                    "type": "string",
                    "description": "Last name.",
                    "example": "Doe"
                  },
                  "middleName": {
                    "type": "string",
                    "description": "Middle name.",
                    "example": ""
                  },
                  "defaultLang": {
                    "type": "string",
                    "description": "Language: en, ru, uk.",
                    "example": "en"
                  },
                  "record_call": {
                    "type": "boolean",
                    "description": "Record calls of the SIP line.",
                    "example": true
                  },
                  "redirectActivity": {
                    "type": "boolean",
                    "description": "Enable redirect of the SIP line.",
                    "example": false
                  },
                  "redirectTo": {
                    "type": "string",
                    "description": "Redirect target number.",
                    "example": "+380441234567"
                  }
                }
              },
              "example": {
                "email": "jane@example.com",
                "username": "jane",
                "password": "Secret123",
                "role": "employee",
                "domain": 3,
                "sipPassword": "SipSecret789",
                "firstName": "Jane",
                "lastName": "Doe",
                "defaultLang": "en",
                "record_call": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created user with its SIP line (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/CompanyUser"
                    },
                    {
                      "type": "object",
                      "description": "Created SIP line.",
                      "properties": {
                        "sip": {
                          "type": "object",
                          "description": "SIP line.",
                          "properties": {
                            "id": {
                              "type": "integer",
                              "description": "SIP line id.",
                              "example": 16
                            },
                            "number": {
                              "type": "string",
                              "description": "3-digit SIP number.",
                              "example": "002"
                            },
                            "password": {
                              "type": "string",
                              "description": "SIP password (returned by the server; masked here).",
                              "example": "********"
                            },
                            "record_call": {
                              "type": "boolean",
                              "description": "Record calls.",
                              "example": true
                            },
                            "redirectActivity": {
                              "type": [
                                "boolean",
                                "null"
                              ],
                              "description": "Redirect enabled.",
                              "example": false
                            },
                            "redirectTo": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Redirect target.",
                              "example": null
                            },
                            "domain": {
                              "type": "object",
                              "description": "SIP domain.",
                              "properties": {
                                "id": {
                                  "type": "integer",
                                  "description": "Domain id.",
                                  "example": 3
                                },
                                "name": {
                                  "type": "string",
                                  "description": "Domain name.",
                                  "example": "examplellc"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "id": 43,
                  "username": "jane",
                  "email": "jane@example.com",
                  "provider": "local",
                  "confirmed": true,
                  "blocked": false,
                  "firstName": "Jane",
                  "lastName": "Doe",
                  "middleName": "",
                  "type": false,
                  "birthDate": null,
                  "defaultLang": "en",
                  "registration_platform": "global",
                  "referral_uid": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
                  "createdAt": "2026-01-20T10:00:00.000Z",
                  "updatedAt": "2026-01-20T10:00:00.000Z",
                  "sip": {
                    "id": 16,
                    "number": "002",
                    "password": "********",
                    "record_call": true,
                    "redirectActivity": false,
                    "redirectTo": null,
                    "domain": {
                      "id": 3,
                      "name": "examplellc"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, duplicate login/e-mail, bad role/domain, employee limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "limit": {
                    "summary": "Tariff employee limit reached",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Max employees restrictions",
                        "details": {}
                      }
                    }
                  },
                  "username": {
                    "summary": "Username taken",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Username already taken",
                        "details": {}
                      }
                    }
                  },
                  "email": {
                    "summary": "E-mail taken",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Email already taken",
                        "details": {}
                      }
                    }
                  },
                  "role": {
                    "summary": "Unknown role name",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Invalid company role error",
                        "details": {}
                      }
                    }
                  },
                  "domain": {
                    "summary": "Domain not found",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Domain not found!",
                        "details": {}
                      }
                    }
                  },
                  "sipFull": {
                    "summary": "No free SIP numbers in the domain",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Всі номери зайняті в данному домені",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/employees\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"email\": \"jane@example.com\",\n  \"username\": \"jane\",\n  \"password\": \"Secret123\",\n  \"role\": \"employee\",\n  \"domain\": 3,\n  \"sipPassword\": \"SipSecret789\",\n  \"firstName\": \"Jane\",\n  \"lastName\": \"Doe\",\n  \"defaultLang\": \"en\",\n  \"record_call\": true\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/employees\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"email\": \"jane@example.com\",\n    \"username\": \"jane\",\n    \"password\": \"Secret123\",\n    \"role\": \"employee\",\n    \"domain\": 3,\n    \"sipPassword\": \"SipSecret789\",\n    \"firstName\": \"Jane\",\n    \"lastName\": \"Doe\",\n    \"defaultLang\": \"en\",\n    \"record_call\": true\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/employees\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"email\":\"jane@example.com\",\"username\":\"jane\",\"password\":\"Secret123\",\"role\":\"employee\",\"domain\":3,\"sipPassword\":\"SipSecret789\",\"firstName\":\"Jane\",\"lastName\":\"Doe\",\"defaultLang\":\"en\",\"record_call\":true}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "companyDeleteEmployees",
        "tags": [
          "Account & Company"
        ],
        "summary": "Delete employees",
        "description": "Deletes the given employees of the company together with their SIP lines. Ids are passed as an array in the\nquery string: `?ids[0]=43&ids[1]=44` (a single plain `ids=43` is rejected). The owner is never deleted.\nEmployees whose SIP line still has phone numbers routed to it, or who are members of a call group, are not deleted\nand are returned in `protected`; employees assigned as redirect targets of callback/recall widgets block the whole\nrequest with 400 — detach them first.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes users and their SIP lines; removes them from notification contacts and unlinks their Telegram.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "description": "Array of user ids: `ids[0]=43&ids[1]=44`.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "example": [
                43,
                44
              ]
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Result per employee.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      },
                      "description": "Ids of deleted users.",
                      "example": [
                        43
                      ]
                    },
                    "failed": {
                      "type": "array",
                      "description": "Users that could not be deleted (internal error).",
                      "items": {
                        "type": "object",
                        "description": "Failure.",
                        "properties": {
                          "id": {
                            "type": "integer",
                            "description": "User id.",
                            "example": 44
                          },
                          "message": {
                            "type": "string",
                            "description": "Error message.",
                            "example": "Internal error"
                          }
                        }
                      }
                    },
                    "protected": {
                      "type": "array",
                      "description": "Users skipped because numbers are routed to their SIP line or they are in a call group.",
                      "items": {
                        "$ref": "#/components/schemas/CompanyUser"
                      }
                    }
                  }
                },
                "example": {
                  "deleted": [
                    43
                  ],
                  "failed": [],
                  "protected": [
                    {
                      "id": 44,
                      "username": "mark",
                      "email": "mark@example.com",
                      "provider": "local",
                      "confirmed": true,
                      "blocked": false,
                      "firstName": "Mark",
                      "lastName": "Doe",
                      "middleName": null,
                      "type": false,
                      "birthDate": null,
                      "defaultLang": "en",
                      "registration_platform": null,
                      "referral_uid": "1a2b3c4d5e6f708192a3b4c5d6e7f809",
                      "createdAt": "2026-01-20T10:00:00.000Z",
                      "updatedAt": "2026-01-20T10:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`ids` is not an array, or an employee is used in a widget.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid": {
                    "summary": "ids missing / not an array",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Invalid params",
                        "details": {}
                      }
                    }
                  },
                  "widgets": {
                    "summary": "Employee is a widget redirect target",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Detach widgets before remove user",
                        "details": {
                          "callbackWidgetName": [
                            [
                              {
                                "callbackWidgetName": "Site widget",
                                "userName": "Jane  Doe"
                              }
                            ]
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/employees?ids=43%2C44\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/employees?ids=43%2C44\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/employees?ids=43%2C44\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/employees/{id}": {
      "put": {
        "operationId": "companyUpdateEmployee",
        "tags": [
          "Account & Company"
        ],
        "summary": "Update an employee",
        "description": "Updates profile fields of an employee of your company. Body uses the Strapi envelope `{ \"data\": {...} }`.\n`company`, `password` and `sip` are silently ignored (use `POST /employees/changePassword/{id}` for the password\nand the SIP endpoints for the line). `role` is the role **id** from `GET /companies/roles`.\nIf the id does not belong to your company nothing is updated and `data` is `null`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** None besides the update.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "User id of the employee.",
            "schema": {
              "type": "integer",
              "example": 43
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Fields to update.",
                    "properties": {
                      "username": {
                        "type": "string",
                        "description": "Login (unique).",
                        "example": "jane"
                      },
                      "email": {
                        "type": "string",
                        "format": "email",
                        "description": "E-mail (unique).",
                        "example": "jane@example.com"
                      },
                      "firstName": {
                        "type": "string",
                        "description": "First name.",
                        "example": "Jane"
                      },
                      "lastName": {
                        "type": "string",
                        "description": "Last name.",
                        "example": "Doe"
                      },
                      "middleName": {
                        "type": "string",
                        "description": "Middle name.",
                        "example": ""
                      },
                      "role": {
                        "type": "integer",
                        "description": "Role id (employee / Superwisor / Finance).",
                        "example": 6
                      },
                      "defaultLang": {
                        "type": "string",
                        "description": "Language: en, ru, uk.",
                        "example": "en"
                      },
                      "birthDate": {
                        "type": "string",
                        "format": "date",
                        "description": "Birth date.",
                        "example": "1990-05-20"
                      },
                      "blocked": {
                        "type": "boolean",
                        "description": "Block the employee (login disabled).",
                        "example": false
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "firstName": "Jane",
                  "lastName": "Doe",
                  "role": 6
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated user (null if not in your company).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "description": "Entity or null.",
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CompanyUserEntity"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 43,
                    "attributes": {
                      "username": "jane",
                      "email": "jane@example.com",
                      "provider": "local",
                      "confirmed": true,
                      "blocked": false,
                      "firstName": "Jane",
                      "lastName": "Doe",
                      "middleName": "",
                      "type": false,
                      "birthDate": null,
                      "defaultLang": "en",
                      "registration_platform": "global",
                      "referral_uid": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
                      "createdAt": "2026-01-20T10:00:00.000Z",
                      "updatedAt": "2026-02-02T09:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Empty body or duplicate login/e-mail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "empty": {
                    "summary": "No data",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Empty Data",
                        "details": {}
                      }
                    }
                  },
                  "username": {
                    "summary": "Username taken",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Username already taken",
                        "details": {}
                      }
                    }
                  },
                  "email": {
                    "summary": "E-mail taken",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Email already taken",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/employees/43\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"firstName\": \"Jane\",\n    \"lastName\": \"Doe\",\n    \"role\": 6\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/employees/43\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"firstName\": \"Jane\",\n      \"lastName\": \"Doe\",\n      \"role\": 6\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/employees/43\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"firstName\":\"Jane\",\"lastName\":\"Doe\",\"role\":6}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/employees/changePassword/{id}": {
      "post": {
        "operationId": "companyChangeEmployeePassword",
        "tags": [
          "Account & Company"
        ],
        "summary": "Set a new password for an employee",
        "description": "Sets a new cabinet password for an employee of your company. Only users with a role not higher than yours can be\nchanged (owner > Superwisor > Finance > employee). Password rules: min 8 chars, lower + upper case letter, digit.\nThe employee receives an e-mail that the password was changed.\n\n**Known issue:** the password is changed and the e-mail is sent, but the server currently answers `500` instead\nof the documented `200` body (internal error when forming the response). Treat 500 as success.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Changes the employee's password; sends them an e-mail notification.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "User id of the employee.",
            "schema": {
              "type": "integer",
              "example": 43
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "password",
                      "confirmPassword"
                    ],
                    "description": "New password.",
                    "properties": {
                      "password": {
                        "type": "string",
                        "format": "password",
                        "description": "New password.",
                        "example": "NewSecret456"
                      },
                      "confirmPassword": {
                        "type": "string",
                        "format": "password",
                        "description": "Repeat (must match).",
                        "example": "NewSecret456"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "password": "NewSecret456",
                  "confirmPassword": "NewSecret456"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed (intended response; see the known issue).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jwt": {
                      "type": "string",
                      "description": "JWT issued for the employee.",
                      "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDN9.sig"
                    },
                    "user": {
                      "$ref": "#/components/schemas/CompanyUser"
                    }
                  }
                },
                "example": {
                  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDN9.sig",
                  "user": {
                    "id": 43,
                    "username": "jane",
                    "email": "jane@example.com",
                    "provider": "local",
                    "confirmed": true,
                    "blocked": false,
                    "firstName": "Jane",
                    "lastName": "Doe",
                    "middleName": "",
                    "type": false,
                    "birthDate": null,
                    "defaultLang": "en",
                    "registration_platform": "global",
                    "referral_uid": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
                    "createdAt": "2026-01-20T10:00:00.000Z",
                    "updatedAt": "2026-02-02T09:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or insufficient permissions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "noData": {
                    "summary": "Body has no data",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Invalid data",
                        "details": {}
                      }
                    }
                  },
                  "mismatch": {
                    "summary": "Confirmation differs",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Repeat password doesn`t match with password",
                        "details": {
                          "errors": [
                            {
                              "path": [
                                "confirmPassword"
                              ],
                              "message": "Repeat password doesn`t match with password",
                              "name": "ValidationError"
                            }
                          ]
                        }
                      }
                    }
                  },
                  "perms": {
                    "summary": "Employee not in company or higher role",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Insufficient permissions",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/employees/changePassword/43\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"password\": \"NewSecret456\",\n    \"confirmPassword\": \"NewSecret456\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/employees/changePassword/43\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"password\": \"NewSecret456\",\n      \"confirmPassword\": \"NewSecret456\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/employees/changePassword/43\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"password\":\"NewSecret456\",\"confirmPassword\":\"NewSecret456\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/delete-account": {
      "delete": {
        "operationId": "companyDeleteAccount",
        "tags": [
          "Account & Company"
        ],
        "summary": "Delete (deactivate) the account",
        "description": "\"Delete account\" for the app stores: **blocks every user of the company** (`blocked: true`) so nobody can log in\nany more; already issued JWTs stop working. Company data, numbers and balance are kept on the server and the\naccount can be restored only by support. Nothing else is deleted.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Blocks all users of the company; the account becomes inaccessible without support.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Users blocked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always true.",
                      "example": true
                    }
                  }
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Company has no users.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "BadRequestError",
                    "message": "Company has no users",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The user has no company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Company not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/companies/delete-account\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/delete-account\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/delete-account\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/register-tickets/register": {
      "post": {
        "operationId": "companySubmitVerification",
        "tags": [
          "Account & Company"
        ],
        "summary": "Submit identity / company verification documents",
        "description": "Creates a verification request (register ticket) for the company — required by regulators before Ukrainian\nnumbers can be used. `multipart/form-data`: field `data` is a **JSON string** with the personal / company details;\nthe documents are files. Which files are required depends on the owner type (`type` of the user):\n\n* **Individual** (`type: false`): `files.withPassport` — selfie with the passport, and at least 3 images in `files.docs[]`.\n* **Sole proprietor** (`type: true`, `isFop: true` in `data`): `files.statue`, `files.Edr`, `files.bankOpening`.\n* **Legal entity** (`type: true`, `isFop` false): `files.registerTaxPayers`, `files.Edr`, `files.bankOpening`,\n  optional `files.decisionAboutDirector`, `files.powerOfAttorney`.\n\n`dateRelease` and `birthDate` are `DD/MM/YYYY`; the passport must be issued at least 14 years after birth.\nAfter review the company `validation` becomes `onProgress` → `yes` / `no` (see `GET /companies/my`).\nRate limit: 20 requests per 5 minutes per IP.\n\n\n**Required role:** Company owner (registered, not yet approved)\n\n**Side effects:** Creates a verification request and uploads the documents for manual review.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "string",
                    "description": "JSON string with the fields of CompanyVerificationData.",
                    "example": "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"birthDate\":\"20/05/1990\",\"passportSeries\":\"AB\",\"passportNumber\":\"123456\",\"whoIssued\":\"1234\",\"dateRelease\":\"10/06/2015\",\"phone\":\"+380441234567\",\"email\":\"owner@example.com\",\"country\":\"UA\",\"city\":\"Kyiv\",\"street\":\"Khreshchatyk\",\"house\":\"1\",\"apartment\":\"2\",\"zip\":\"01001\"}"
                  },
                  "files.withPassport": {
                    "type": "string",
                    "format": "binary",
                    "description": "Individual: selfie with passport (image)."
                  },
                  "files.docs[]": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    },
                    "description": "Individual: at least 3 document images."
                  },
                  "files.statue": {
                    "type": "string",
                    "format": "binary",
                    "description": "Sole proprietor: statute / registration extract."
                  },
                  "files.Edr": {
                    "type": "string",
                    "format": "binary",
                    "description": "Company: EDR extract."
                  },
                  "files.bankOpening": {
                    "type": "string",
                    "format": "binary",
                    "description": "Company: bank account certificate."
                  },
                  "files.registerTaxPayers": {
                    "type": "string",
                    "format": "binary",
                    "description": "Legal entity: tax payer registration extract."
                  },
                  "files.decisionAboutDirector": {
                    "type": "string",
                    "format": "binary",
                    "description": "Legal entity, optional: decision on director appointment."
                  },
                  "files.powerOfAttorney": {
                    "type": "string",
                    "format": "binary",
                    "description": "Legal entity, optional: power of attorney."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created verification request.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CompanyVerificationTicket"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 12,
                    "attributes": {
                      "firstName": "John",
                      "lastName": "Doe",
                      "birthDate": "20/05/1990",
                      "passportSeries": "AB",
                      "passportNumber": "123456",
                      "whoIssued": "1234",
                      "dateRelease": "10/06/2015",
                      "IdPassportNumber": null,
                      "phone": "+380441234567",
                      "email": "owner@example.com",
                      "country": "UA",
                      "zip": "01001",
                      "state": null,
                      "city": "Kyiv",
                      "street": "Khreshchatyk",
                      "house": "1",
                      "apartment": "2",
                      "YRGPO": "",
                      "ticket_status": null,
                      "createdAt": "2026-02-02T09:00:00.000Z",
                      "updatedAt": "2026-02-02T09:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Missing documents or invalid dates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "noPassport": {
                    "summary": "Individual without selfie",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Необхідно завантажити фото з паспортом!",
                        "details": {}
                      }
                    }
                  },
                  "fewDocs": {
                    "summary": "Fewer than 3 documents",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Необхідно завантажити 3 фото або сканів документів!",
                        "details": {}
                      }
                    }
                  },
                  "companyDocs": {
                    "summary": "Company document missing",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Необхідно завантажити кожне фото !",
                        "details": {}
                      }
                    }
                  },
                  "dates": {
                    "summary": "Passport issued less than 14 years after birth",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Різниця між датою народження та датою випуску паспорту має бути більше 14 років!",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/register-tickets/register\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/register-tickets/register\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/register-tickets/register\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/company-audios": {
      "get": {
        "operationId": "companyAudioList",
        "tags": [
          "Account & Company"
        ],
        "summary": "List audio files (own or system library)",
        "description": "Audio files used in IVR menus and call groups (greetings — `type: sound`, music on hold — `type: moh`).\n`category=additional` (default) returns the files uploaded by your company with their `owner`;\n`category=base` returns the shared system library available to everyone. Standard pagination, sort and filters\napply (e.g. `filters[type][$eq]=moh`). The upload file object is populated in `file`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "`additional` — company files (default), `base` — system library.",
            "schema": {
              "type": "string",
              "enum": [
                "additional",
                "base"
              ],
              "default": "additional",
              "example": "additional"
            }
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          }
        ],
        "responses": {
          "200": {
            "description": "Audio files.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Audio files.",
                      "items": {
                        "$ref": "#/components/schemas/CompanyAudio"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 156,
                      "attributes": {
                        "name": "Greeting",
                        "type": "sound",
                        "category": "additional",
                        "url": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav",
                        "size": 154.69,
                        "createdAt": "2026-01-20T12:08:36.711Z",
                        "updatedAt": "2026-01-20T12:08:36.711Z",
                        "file": {
                          "data": {
                            "id": 1001,
                            "attributes": {
                              "name": "greeting.wav",
                              "ext": ".wav",
                              "mime": "audio/mpeg",
                              "size": 154.69,
                              "url": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav",
                              "hash": "greeting_wav_a1b2c3",
                              "provider": "local",
                              "createdAt": "2026-01-20T12:08:30.000Z",
                              "updatedAt": "2026-01-20T12:08:36.000Z"
                            }
                          }
                        },
                        "owner": {
                          "data": {
                            "id": 42,
                            "attributes": {
                              "username": "owner",
                              "email": "owner@example.com",
                              "firstName": "John",
                              "lastName": "Doe"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/company-audios\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/company-audios\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/company-audios\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "companyAudioUpload",
        "tags": [
          "Account & Company"
        ],
        "summary": "Upload an audio file",
        "description": "Uploads an MP3 (`audio/mpeg`, up to 20 MB) as `multipart/form-data` with fields `file`, `name`, `type`.\nThe file is converted asynchronously to 8 kHz mono WAV for the PBX; `url`/`size` are filled after conversion\n(usually within a few seconds), the original MP3 is then removed. Always send `category: additional` or omit it.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Stores the file, schedules conversion to WAV, reloads audio on the PBX.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "name",
                  "type"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "MP3 file (audio/mpeg), max 20 MB."
                  },
                  "name": {
                    "type": "string",
                    "description": "Display name.",
                    "example": "Greeting"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "sound",
                      "moh"
                    ],
                    "description": "sound — announcement / greeting, moh — music on hold.",
                    "example": "sound"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "additional"
                    ],
                    "description": "Always `additional` (company file).",
                    "example": "additional"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created record (url/size appear after conversion).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CompanyAudio"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 157,
                    "attributes": {
                      "name": "Greeting",
                      "type": "sound",
                      "category": "additional",
                      "url": null,
                      "size": null,
                      "createdAt": "2026-02-02T09:00:00.000Z",
                      "updatedAt": "2026-02-02T09:00:00.000Z",
                      "file": {
                        "data": {
                          "id": 1002,
                          "attributes": {
                            "name": "greeting.mp3",
                            "ext": ".mp3",
                            "mime": "audio/mpeg",
                            "size": 310.5,
                            "url": "/uploads/greeting_mp3_d4e5f6.mp3",
                            "hash": "greeting_mp3_d4e5f6",
                            "provider": "local",
                            "createdAt": "2026-02-02T09:00:00.000Z",
                            "updatedAt": "2026-02-02T09:00:00.000Z"
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "File missing, too large or not MP3.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing": {
                    "summary": "No file",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Missing file",
                        "details": {}
                      }
                    }
                  },
                  "size": {
                    "summary": "Too large",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "File size must be not more than 20mb",
                        "details": {}
                      }
                    }
                  },
                  "type": {
                    "summary": "Not audio/mpeg",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Invalid file type",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/company-audios\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/company-audios\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/company-audios\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/company-audios/{id}": {
      "get": {
        "operationId": "companyAudioGet",
        "tags": [
          "Account & Company"
        ],
        "summary": "Get an audio file",
        "description": "Returns one audio file with `file` and `owner` populated. System library files (`category: base`) are visible to\neveryone; a company file of another company yields 400 \"Not found\"; a non-existent id yields `data: null`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Audio file id.",
            "schema": {
              "type": "integer",
              "example": 156
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audio file or null.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "description": "Entity or null.",
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CompanyAudio"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 156,
                    "attributes": {
                      "name": "Greeting",
                      "type": "sound",
                      "category": "additional",
                      "url": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav",
                      "size": 154.69,
                      "createdAt": "2026-01-20T12:08:36.711Z",
                      "updatedAt": "2026-01-20T12:08:36.711Z",
                      "file": {
                        "data": {
                          "id": 1001,
                          "attributes": {
                            "name": "greeting.wav",
                            "ext": ".wav",
                            "mime": "audio/mpeg",
                            "size": 154.69,
                            "url": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav",
                            "hash": "greeting_wav_a1b2c3",
                            "provider": "local",
                            "createdAt": "2026-01-20T12:08:30.000Z",
                            "updatedAt": "2026-01-20T12:08:36.000Z"
                          }
                        }
                      },
                      "owner": {
                        "data": {
                          "id": 42,
                          "attributes": {
                            "username": "owner",
                            "email": "owner@example.com",
                            "firstName": "John",
                            "lastName": "Doe"
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "File belongs to another company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/company-audios/156\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/company-audios/156\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/company-audios/156\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "companyAudioUpdate",
        "tags": [
          "Account & Company"
        ],
        "summary": "Rename / change an audio file",
        "description": "Updates `name` / `type` of a company audio file. The body is **flat** (no `data` envelope); JSON or\n`multipart/form-data` with an optional new `file`. Only files of your company are updated — otherwise `data` is `null`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Reloads audio on the PBX.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Audio file id.",
            "schema": {
              "type": "integer",
              "example": 156
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Display name.",
                    "example": "Greeting v2"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "sound",
                      "moh"
                    ],
                    "description": "sound or moh.",
                    "example": "sound"
                  }
                }
              },
              "example": {
                "name": "Greeting v2"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated record (null if not yours).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "description": "Entity or null.",
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CompanyAudio"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 156,
                    "attributes": {
                      "name": "Greeting v2",
                      "type": "sound",
                      "category": "additional",
                      "url": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav",
                      "size": 154.69,
                      "createdAt": "2026-01-20T12:08:36.711Z",
                      "updatedAt": "2026-02-02T09:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/company-audios/156\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"name\": \"Greeting v2\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/company-audios/156\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"name\": \"Greeting v2\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/company-audios/156\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"name\":\"Greeting v2\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "companyAudioDelete",
        "tags": [
          "Account & Company"
        ],
        "summary": "Delete an audio file",
        "description": "Deletes a company audio file and its stored file. Files that are still used by a call group cannot be deleted\n(400 with the entity in `data`); detach them first. Files of other companies / system files → 400 \"Not found\".\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the record and the file; reloads audio on the PBX.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Audio file id.",
            "schema": {
              "type": "integer",
              "example": 156
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted record.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CompanyAudio"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 156,
                    "attributes": {
                      "name": "Greeting",
                      "type": "sound",
                      "category": "additional",
                      "url": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav",
                      "size": 154.69,
                      "createdAt": "2026-01-20T12:08:36.711Z",
                      "updatedAt": "2026-01-20T12:08:36.711Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Not found in your company, or still attached to a group (non-standard body).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Either the standard error envelope or `{message, data}`.",
                  "properties": {
                    "message": {
                      "type": "string",
                      "description": "Reason.",
                      "example": "Detach file from group before delete"
                    },
                    "data": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "`{ entity }` — the audio with populated group relations; `null` in the standard error envelope.",
                      "example": {
                        "entity": {
                          "id": 156,
                          "name": "Greeting"
                        }
                      }
                    },
                    "error": {
                      "type": "object",
                      "description": "Error details (standard envelope only, see Error.error).",
                      "example": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Not found",
                        "details": {}
                      }
                    }
                  }
                },
                "examples": {
                  "attached": {
                    "summary": "Still used by a group",
                    "value": {
                      "message": "Detach file from group before delete",
                      "data": {
                        "entity": {
                          "id": 156,
                          "name": "Greeting",
                          "type": "sound",
                          "category": "additional",
                          "groups_musiconhold": [
                            {
                              "id": 9,
                              "name": "Sales"
                            }
                          ]
                        }
                      }
                    }
                  },
                  "notFound": {
                    "summary": "Not your file",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Not found",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/company-audios/156\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/company-audios/156\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/company-audios/156\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/company-audios/bulk-delete": {
      "delete": {
        "operationId": "companyAudioBulkDelete",
        "tags": [
          "Account & Company"
        ],
        "summary": "Delete several audio files",
        "description": "Deletes several company audio files at once. Ids go in the query string: `?ids[0]=156&ids[1]=157`.\nFiles still attached to a call group (or not belonging to your company) are returned in `failed` and kept.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes records and files; reloads audio on the PBX.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "description": "Array of audio ids: `ids[0]=156&ids[1]=157`.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "example": [
                156,
                157
              ]
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted and skipped files (flat objects).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "array",
                      "description": "Deleted files.",
                      "items": {
                        "$ref": "#/components/schemas/CompanyAudioFlat"
                      }
                    },
                    "failed": {
                      "type": "array",
                      "description": "Files kept (attached to a group or error).",
                      "items": {
                        "$ref": "#/components/schemas/CompanyAudioFlat"
                      }
                    }
                  }
                },
                "example": {
                  "success": [
                    {
                      "id": 156,
                      "name": "Greeting",
                      "type": "sound",
                      "category": "additional",
                      "url": "/uploads/sound/company_7/greeting.wav/greeting_wav_a1b2c3.wav",
                      "size": 154.69,
                      "createdAt": "2026-01-20T12:08:36.711Z",
                      "updatedAt": "2026-01-20T12:08:36.711Z"
                    }
                  ],
                  "failed": []
                }
              }
            }
          },
          "400": {
            "description": "`ids` missing or not an array.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Field ids is empty or has invalid type",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/company-audios/bulk-delete?ids=156%2C157\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/company-audios/bulk-delete?ids=156%2C157\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/company-audios/bulk-delete?ids=156%2C157\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/company-audios/findTotalSize": {
      "get": {
        "operationId": "companyAudioTotalSize",
        "tags": [
          "Account & Company"
        ],
        "summary": "Total size of audio files",
        "description": "Returns the sum of `size` of the company's audio files (`category=additional`, default) or of the system library\n(`category=base`). Used to show storage usage.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "`additional` (default) or `base`.",
            "schema": {
              "type": "string",
              "enum": [
                "additional",
                "base"
              ],
              "default": "additional",
              "example": "additional"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Total size.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "size": {
                      "type": "number",
                      "description": "Sum of file sizes (same units as `size` of the files, kilobytes).",
                      "example": 2697.81
                    }
                  }
                },
                "example": {
                  "size": 2697.81
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/company-audios/findTotalSize\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/company-audios/findTotalSize\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/company-audios/findTotalSize\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/users/{id}": {
      "get": {
        "operationId": "companyGetUser",
        "tags": [
          "Account & Company"
        ],
        "summary": "Get a user by id",
        "description": "Returns a user record as a flat object (no `data` envelope). Use `populate` to include `role`, `company`, `sip`.\nIntended for reading employees of your own company (e.g. after `GET /companies/getEmployees`).\nPrivate fields (password, reset tokens) are never returned with values.\n\n\n**Required role:** Employee, Public, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "User id.",
            "schema": {
              "type": "integer",
              "example": 43
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "User.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompanyUser"
                },
                "example": {
                  "id": 43,
                  "username": "jane",
                  "email": "jane@example.com",
                  "provider": "local",
                  "confirmed": true,
                  "blocked": false,
                  "firstName": "Jane",
                  "lastName": "Doe",
                  "middleName": null,
                  "type": false,
                  "birthDate": null,
                  "defaultLang": "en",
                  "registration_platform": null,
                  "referral_uid": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
                  "createdAt": "2026-01-20T10:00:00.000Z",
                  "updatedAt": "2026-01-20T10:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/users/43\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/users/43\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/users/43\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "companyUpdateUser",
        "tags": [
          "Account & Company"
        ],
        "summary": "Update a user record (own profile)",
        "description": "Core Strapi user update. The body is **flat** (no `data` envelope). Use it to update your own profile fields:\n`firstName`, `lastName`, `middleName`, `birthDate`, `defaultLang`, `username`, `email`.\nTo manage employees prefer `PUT /employees/{id}` (restricted to your company) and\n`POST /employees/changePassword/{id}`. Do not send `role`, `company` or `sip` here.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** Updates the user; a new `password`, if sent, replaces the current one.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "User id (your own id from GET /users/me).",
            "schema": {
              "type": "integer",
              "example": 42
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "username": {
                    "type": "string",
                    "description": "Login (unique).",
                    "example": "owner"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "E-mail (unique, stored lower-case).",
                    "example": "owner@example.com"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "description": "New password (optional).",
                    "example": "NewSecret456"
                  },
                  "firstName": {
                    "type": "string",
                    "description": "First name.",
                    "example": "John"
                  },
                  "lastName": {
                    "type": "string",
                    "description": "Last name.",
                    "example": "Doe"
                  },
                  "middleName": {
                    "type": "string",
                    "description": "Middle name.",
                    "example": ""
                  },
                  "birthDate": {
                    "type": "string",
                    "format": "date",
                    "description": "Birth date.",
                    "example": "1990-05-20"
                  },
                  "defaultLang": {
                    "type": "string",
                    "description": "Language: en, ru, uk.",
                    "example": "uk"
                  }
                }
              },
              "example": {
                "firstName": "John",
                "lastName": "Doe",
                "defaultLang": "uk"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated user (flat).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompanyUser"
                },
                "example": {
                  "id": 42,
                  "username": "owner",
                  "email": "owner@example.com",
                  "provider": "local",
                  "confirmed": true,
                  "blocked": false,
                  "firstName": "John",
                  "lastName": "Doe",
                  "middleName": null,
                  "type": false,
                  "birthDate": null,
                  "defaultLang": "uk",
                  "registration_platform": "global",
                  "referral_uid": "f5436f2e0178004a599a96209c746f78",
                  "createdAt": "2026-01-15T10:00:00.000Z",
                  "updatedAt": "2026-02-02T09:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Duplicate login/e-mail or empty password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "username": {
                    "summary": "Username taken",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Username already taken",
                        "details": {}
                      }
                    }
                  },
                  "email": {
                    "summary": "E-mail taken",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Email already taken",
                        "details": {}
                      }
                    }
                  },
                  "password": {
                    "summary": "Empty password sent",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "password.notNull",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "User not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "User not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/users/42\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"firstName\": \"John\",\n  \"lastName\": \"Doe\",\n  \"defaultLang\": \"uk\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/users/42\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"firstName\": \"John\",\n    \"lastName\": \"Doe\",\n    \"defaultLang\": \"uk\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/users/42\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"firstName\":\"John\",\"lastName\":\"Doe\",\"defaultLang\":\"uk\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/auth/local": {
      "post": {
        "operationId": "authLogin",
        "tags": [
          "Auth"
        ],
        "summary": "Log in (email/username + password)",
        "description": "Authenticates a user by email (or username) and password and returns a JWT together with the user profile.\nPass the JWT in every subsequent request as `Authorization: Bearer <jwt>`.\n\n**Token lifetime:** the JWT is valid for **30 days** (`expiresIn: 30d`). There is **no refresh endpoint** —\nwhen the token expires (401) the client must call `/auth/local` again. `POST /auth/logout` does not revoke the token.\n\nIf the request is sent from a browser whose `Origin` is the official web cabinet, a Google reCAPTCHA v2\nresponse must be passed in `captcha`. Server-to-server integrations (no `Origin` header) do not need it.\n\n\n**Required role:** Public, Employee, Company owner (registered, not yet approved), Company owner (approved), Supervisor, Finance\n\n**Side effects:** None (the login is written to the server log).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "identifier",
                  "password"
                ],
                "properties": {
                  "identifier": {
                    "type": "string",
                    "description": "Email or username.",
                    "example": "owner@example.com"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "description": "Password.",
                    "example": "Secret123"
                  },
                  "captcha": {
                    "type": "string",
                    "description": "Google reCAPTCHA v2 response. Required only for browser requests from the official web cabinet.",
                    "example": "03AGdBq2...."
                  }
                }
              },
              "example": {
                "identifier": "owner@example.com",
                "password": "Secret123"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JWT and user profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSession"
                },
                "example": {
                  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDIsImlhdCI6MTc1ODAwMDAwMCwiZXhwIjoxNzYwNTkyMDAwfQ.sig",
                  "user": {
                    "id": 42,
                    "username": "owner",
                    "email": "owner@example.com",
                    "provider": "local",
                    "confirmed": true,
                    "blocked": false,
                    "firstName": "John",
                    "lastName": "Doe",
                    "middleName": null,
                    "type": false,
                    "birthDate": null,
                    "defaultLang": "en",
                    "registration_platform": "global",
                    "referral_uid": "f5436f2e0178004a599a96209c746f78",
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "updatedAt": "2026-01-15T10:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Wrong credentials, blocked / unconfirmed account, captcha error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidCredentials": {
                    "summary": "Invalid identifier or password",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Invalid identifier or password",
                        "details": {}
                      }
                    }
                  },
                  "blocked": {
                    "summary": "Account blocked",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Your account has been blocked by an administrator",
                        "details": {}
                      }
                    }
                  },
                  "notConfirmed": {
                    "summary": "Email not confirmed",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Your account email is not confirmed",
                        "details": {}
                      }
                    }
                  },
                  "captcha": {
                    "summary": "Captcha missing / invalid",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Invalid captcha!",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/auth/local\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"identifier\": \"owner@example.com\",\n  \"password\": \"Secret123\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/auth/local\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"identifier\": \"owner@example.com\",\n    \"password\": \"Secret123\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/auth/local\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"identifier\":\"owner@example.com\",\"password\":\"Secret123\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/auth/local/register": {
      "post": {
        "operationId": "authRegister",
        "tags": [
          "Auth"
        ],
        "summary": "Register a new account (company owner)",
        "description": "Creates a new user with the default role *Company owner* and automatically creates a company for them.\nThe company name is built from `lastName firstName middleName`; if none are given, `username` is used;\nfor a legal entity (`type: true`) the `Name` field is used. Billing currency and payment acquirer are chosen\nby the client IP: Ukraine → UAH / WayForPay, other countries → USD / Stripe. Mobile registrations\n(mobile `User-Agent`) get the *Personal* tariff and an active company immediately.\n\nPassword rules: at least 8 characters, at least one lowercase letter, one uppercase letter and one digit.\n\nIf e-mail confirmation is enabled on the server, the response contains only `user` (no `jwt`) and the user\nmust confirm the e-mail first; otherwise `jwt` (30 days, no refresh) is returned right away.\nBrowser requests from the official web cabinet must include `captcha` (reCAPTCHA v2).\n\n\n**Required role:** Public, Company owner (registered, not yet approved)\n\n**Side effects:** Creates a user and a company; sends a welcome e-mail; sends an internal registration notification to the OneVOIPlanet team.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "username",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "E-mail (stored lower-case, must be unique).",
                    "example": "owner@example.com"
                  },
                  "username": {
                    "type": "string",
                    "description": "Login (unique). The cabinet usually sends the phone number here.",
                    "example": "+380441234567"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "description": "Password: min 8 chars, lower + upper case letter and a digit.",
                    "example": "Secret123"
                  },
                  "firstName": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "First name.",
                    "example": "John"
                  },
                  "lastName": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Last name.",
                    "example": "Doe"
                  },
                  "middleName": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Middle name.",
                    "example": ""
                  },
                  "type": {
                    "type": "boolean",
                    "description": "`true` — legal entity (company), `false` — individual. Default false.",
                    "example": false
                  },
                  "Name": {
                    "type": "string",
                    "description": "Company name (used when `type: true`).",
                    "example": "Example LLC"
                  },
                  "YRGPO": {
                    "type": "string",
                    "description": "Company registration number (EDRPOU) for a legal entity.",
                    "example": "12345678"
                  },
                  "defaultLang": {
                    "type": "string",
                    "description": "Interface / notification language: en, ru, uk.",
                    "example": "en"
                  },
                  "ref": {
                    "type": "string",
                    "description": "Referral code of the inviting user (`referral_uid` of their profile).",
                    "example": "f5436f2e0178004a599a96209c746f78"
                  },
                  "captcha": {
                    "type": "string",
                    "description": "Google reCAPTCHA v2 response (browser requests from the web cabinet only).",
                    "example": "03AGdBq2...."
                  }
                }
              },
              "example": {
                "email": "owner@example.com",
                "username": "+380441234567",
                "password": "Secret123",
                "firstName": "John",
                "lastName": "Doe",
                "type": false,
                "defaultLang": "en"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created user. `jwt` is absent when e-mail confirmation is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSession"
                },
                "example": {
                  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDIsImlhdCI6MTc1ODAwMDAwMCwiZXhwIjoxNzYwNTkyMDAwfQ.sig",
                  "user": {
                    "id": 42,
                    "username": "+380441234567",
                    "email": "owner@example.com",
                    "provider": "local",
                    "confirmed": true,
                    "blocked": false,
                    "firstName": "John",
                    "lastName": "Doe",
                    "middleName": null,
                    "type": false,
                    "birthDate": null,
                    "defaultLang": "en",
                    "registration_platform": "global",
                    "referral_uid": "9a7b1c2d3e4f5a6b7c8d9e0f1a2b3c4d",
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "updatedAt": "2026-01-15T10:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, e-mail/username already taken, registration disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "taken": {
                    "summary": "E-mail or username already used",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "The post or number is already taken",
                        "details": {}
                      }
                    }
                  },
                  "password": {
                    "summary": "Password does not match the rules",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "password must match the following: \"/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[A-Za-z\\d\\S]{8,}$/\"",
                        "details": {
                          "errors": [
                            {
                              "path": [
                                "password"
                              ],
                              "message": "password must match the following: \"/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[A-Za-z\\d\\S]{8,}$/\"",
                              "name": "ValidationError"
                            }
                          ]
                        }
                      }
                    }
                  },
                  "disabled": {
                    "summary": "Registration disabled on the server",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Реєстрація вимкнена",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/auth/local/register\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"email\": \"owner@example.com\",\n  \"username\": \"+380441234567\",\n  \"password\": \"Secret123\",\n  \"firstName\": \"John\",\n  \"lastName\": \"Doe\",\n  \"type\": false,\n  \"defaultLang\": \"en\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/auth/local/register\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"email\": \"owner@example.com\",\n    \"username\": \"+380441234567\",\n    \"password\": \"Secret123\",\n    \"firstName\": \"John\",\n    \"lastName\": \"Doe\",\n    \"type\": false,\n    \"defaultLang\": \"en\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/auth/local/register\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"email\":\"owner@example.com\",\"username\":\"+380441234567\",\"password\":\"Secret123\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"type\":false,\"defaultLang\":\"en\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/auth/logout": {
      "post": {
        "operationId": "authLogout",
        "tags": [
          "Auth"
        ],
        "summary": "Log out",
        "description": "Always returns `204 No Content` and does nothing on the server: JWTs are stateless and are **not revoked**.\nThe client must delete the stored token itself. Call `DELETE /firebase-tokens/{token}` beforehand if the device\nshould stop receiving push notifications.\n\n\n**Required role:** no token required (public)\n\n**Side effects:** None",
        "security": [],
        "responses": {
          "204": {
            "description": "Logged out (no body)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/auth/logout\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/auth/logout\", {\n  method: \"POST\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/auth/logout\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/auth/change-password": {
      "post": {
        "operationId": "authChangePassword",
        "tags": [
          "Auth"
        ],
        "summary": "Change own password",
        "description": "Changes the password of the authenticated user after checking the current one. The new password must differ\nfrom the current one and satisfy the rules (min 8 chars, lower + upper case letter, digit).\nReturns a fresh JWT; previously issued JWTs stay valid until they expire.\n\n\n**Required role:** Employee, Company owner (registered, not yet approved), Company owner (approved), Supervisor, Finance\n\n**Side effects:** Changes the user's password.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currentPassword",
                  "password",
                  "passwordConfirmation"
                ],
                "properties": {
                  "currentPassword": {
                    "type": "string",
                    "format": "password",
                    "description": "Current password.",
                    "example": "Secret123"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "description": "New password.",
                    "example": "NewSecret456"
                  },
                  "passwordConfirmation": {
                    "type": "string",
                    "format": "password",
                    "description": "Repeat of the new password (must match).",
                    "example": "NewSecret456"
                  }
                }
              },
              "example": {
                "currentPassword": "Secret123",
                "password": "NewSecret456",
                "passwordConfirmation": "NewSecret456"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New JWT and user profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSession"
                },
                "example": {
                  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDIsImlhdCI6MTc1ODAwMDAwMCwiZXhwIjoxNzYwNTkyMDAwfQ.sig",
                  "user": {
                    "id": 42,
                    "username": "owner",
                    "email": "owner@example.com",
                    "provider": "local",
                    "confirmed": true,
                    "blocked": false,
                    "firstName": "John",
                    "lastName": "Doe",
                    "middleName": null,
                    "type": false,
                    "birthDate": null,
                    "defaultLang": "en",
                    "registration_platform": "global",
                    "referral_uid": "f5436f2e0178004a599a96209c746f78",
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "updatedAt": "2026-02-01T12:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "wrongCurrent": {
                    "summary": "Current password is wrong",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "The provided current password is invalid",
                        "details": {}
                      }
                    }
                  },
                  "same": {
                    "summary": "New password equals the current one",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Your new password must be different than your current password",
                        "details": {}
                      }
                    }
                  },
                  "mismatch": {
                    "summary": "Confirmation does not match",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Passwords do not match",
                        "details": {
                          "errors": [
                            {
                              "path": [
                                "passwordConfirmation"
                              ],
                              "message": "Passwords do not match",
                              "name": "ValidationError"
                            }
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/auth/change-password\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"currentPassword\": \"Secret123\",\n  \"password\": \"NewSecret456\",\n  \"passwordConfirmation\": \"NewSecret456\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/auth/change-password\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"currentPassword\": \"Secret123\",\n    \"password\": \"NewSecret456\",\n    \"passwordConfirmation\": \"NewSecret456\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/auth/change-password\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"currentPassword\":\"Secret123\",\"password\":\"NewSecret456\",\"passwordConfirmation\":\"NewSecret456\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/auth/forgot-password": {
      "post": {
        "operationId": "authForgotPassword",
        "tags": [
          "Auth"
        ],
        "summary": "Request a password reset code",
        "description": "Step 1 of password recovery. Generates a 6-digit code, e-mails it to the user and returns a one-time `token`.\nThe client then calls `POST /auth/validate-code` (optional check) and `POST /auth/reset-password` with the\nsame `token` and the `code` from the e-mail. Each call invalidates the previous token/code pair.\nUse `?locale=` to choose the e-mail language.\n\n\n**Required role:** Public, Company owner (registered, not yet approved)\n\n**Side effects:** Sends an e-mail with a 6-digit code; invalidates a previously issued reset code.",
        "security": [],
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "E-mail language: en, ru, uk. Default — the user's `defaultLang`.",
            "schema": {
              "type": "string",
              "example": "en"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "E-mail of the account.",
                    "example": "owner@example.com"
                  },
                  "isCurrentLocale": {
                    "type": "boolean",
                    "description": "Reserved flag of the web cabinet; may be omitted.",
                    "example": true
                  }
                }
              },
              "example": {
                "email": "owner@example.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code sent; `token` must be stored for the next steps.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always true.",
                      "example": true
                    },
                    "token": {
                      "type": "string",
                      "description": "One-time reset token (128 hex chars) to pass to validate-code / reset-password.",
                      "example": "3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a"
                    }
                  }
                },
                "example": {
                  "ok": true,
                  "token": "3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a"
                }
              }
            }
          },
          "400": {
            "description": "Unknown e-mail or blocked user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "No user with this e-mail",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "User with this email does not exist!",
                        "details": {}
                      }
                    }
                  },
                  "blocked": {
                    "summary": "User is blocked",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "User is blocked!",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/auth/forgot-password\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"email\": \"owner@example.com\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/auth/forgot-password\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"email\": \"owner@example.com\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/auth/forgot-password\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"email\":\"owner@example.com\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/auth/validate-code": {
      "post": {
        "operationId": "authValidateResetCode",
        "tags": [
          "Auth"
        ],
        "summary": "Check a password reset code",
        "description": "Step 2 (optional) of password recovery: checks that the `token` from `/auth/forgot-password` and the 6-digit\n`code` from the e-mail match, without changing anything. Rate limit: 5 requests per 5 minutes per IP.\n\n\n**Required role:** no token required (public)\n\n**Side effects:** None",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "code"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Token returned by /auth/forgot-password.",
                    "example": "3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f…"
                  },
                  "code": {
                    "type": "integer",
                    "description": "6-digit code from the e-mail.",
                    "example": 482913
                  }
                }
              },
              "example": {
                "token": "3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a",
                "code": 482913
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token and code are valid.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always true.",
                      "example": true
                    }
                  }
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Wrong token/code pair.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Надіслано невірний код!",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/auth/validate-code\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"token\": \"3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\",\n  \"code\": 482913\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/auth/validate-code\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"token\": \"3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\",\n    \"code\": 482913\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/auth/validate-code\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"token\":\"3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\",\"code\":482913}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/auth/reset-password": {
      "post": {
        "operationId": "authResetPassword",
        "tags": [
          "Auth"
        ],
        "summary": "Set a new password by reset code",
        "description": "Step 3 of password recovery: sets a new password for the user identified by the `token` from\n`/auth/forgot-password` and the 6-digit `code` from the e-mail. The token/code pair is invalidated.\nReturns a JWT (30 days) and the user profile, so the client is logged in immediately.\n\n\n**Required role:** Public, Company owner (registered, not yet approved)\n\n**Side effects:** Changes the password; sends an e-mail \"your password has been changed\".",
        "security": [],
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "E-mail language: en, ru, uk.",
            "schema": {
              "type": "string",
              "example": "en"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "password",
                  "passwordConfirmation",
                  "token",
                  "code"
                ],
                "properties": {
                  "password": {
                    "type": "string",
                    "format": "password",
                    "description": "New password (min 8 chars, lower + upper case letter, digit).",
                    "example": "NewSecret456"
                  },
                  "passwordConfirmation": {
                    "type": "string",
                    "format": "password",
                    "description": "Repeat of the new password.",
                    "example": "NewSecret456"
                  },
                  "token": {
                    "type": "string",
                    "description": "Token from /auth/forgot-password.",
                    "example": "3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f…"
                  },
                  "code": {
                    "type": "integer",
                    "description": "6-digit code from the e-mail.",
                    "example": 482913
                  },
                  "isCurrentLocale": {
                    "type": "boolean",
                    "description": "Reserved flag of the web cabinet; may be omitted.",
                    "example": true
                  }
                }
              },
              "example": {
                "password": "NewSecret456",
                "passwordConfirmation": "NewSecret456",
                "token": "3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a",
                "code": 482913
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed; JWT and user profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSession"
                },
                "example": {
                  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDIsImlhdCI6MTc1ODAwMDAwMCwiZXhwIjoxNzYwNTkyMDAwfQ.sig",
                  "user": {
                    "id": 42,
                    "username": "owner",
                    "email": "owner@example.com",
                    "provider": "local",
                    "confirmed": true,
                    "blocked": false,
                    "firstName": "John",
                    "lastName": "Doe",
                    "middleName": null,
                    "type": false,
                    "birthDate": null,
                    "defaultLang": "en",
                    "registration_platform": "global",
                    "referral_uid": "f5436f2e0178004a599a96209c746f78",
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "updatedAt": "2026-02-01T12:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Wrong token/code, passwords mismatch or weak password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "wrongCode": {
                    "summary": "Token/code pair not found",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Incorrect token and code provided",
                        "details": {}
                      }
                    }
                  },
                  "mismatch": {
                    "summary": "Confirmation does not match",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Passwords do not match",
                        "details": {
                          "errors": [
                            {
                              "path": [
                                "passwordConfirmation"
                              ],
                              "message": "Passwords do not match",
                              "name": "ValidationError"
                            }
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/auth/reset-password\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"password\": \"NewSecret456\",\n  \"passwordConfirmation\": \"NewSecret456\",\n  \"token\": \"3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\",\n  \"code\": 482913\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/auth/reset-password\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"password\": \"NewSecret456\",\n    \"passwordConfirmation\": \"NewSecret456\",\n    \"token\": \"3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\",\n    \"code\": 482913\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/auth/reset-password\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"password\":\"NewSecret456\",\"passwordConfirmation\":\"NewSecret456\",\"token\":\"3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a3f1a9c0e5b7d2a4c6e8f0b1d3a5c7e9f1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\",\"code\":482913}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/users/me": {
      "get": {
        "operationId": "authGetMe",
        "tags": [
          "Auth"
        ],
        "summary": "Current user profile",
        "description": "Returns the profile of the user identified by the JWT, as a flat object (no `data/attributes` envelope).\nBy default the role with its permission list, the company and the SIP line (with its domain) are populated.\nPass `populate[role]=false` to get a lighter response without the permissions list, or your own `populate`\nto choose relations (`company`, `sip`, `sip.domain`, `firebase_tokens`, ...).\nRequires a valid token (401 otherwise). Use it on app start to detect the role (`role.type`) and the company id.\n\n\n**Required role:** Employee, Public, Company owner (registered, not yet approved), Company owner (approved), Supervisor, Finance\n\n**Side effects:** None (refreshes the internal user → company/SIP cache).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "User profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthMe"
                },
                "example": {
                  "id": 42,
                  "username": "owner",
                  "email": "owner@example.com",
                  "provider": "local",
                  "confirmed": true,
                  "blocked": false,
                  "firstName": "John",
                  "lastName": "Doe",
                  "middleName": null,
                  "type": false,
                  "birthDate": null,
                  "defaultLang": "en",
                  "registration_platform": "global",
                  "referral_uid": "f5436f2e0178004a599a96209c746f78",
                  "createdAt": "2026-01-15T10:00:00.000Z",
                  "updatedAt": "2026-02-01T12:00:00.000Z",
                  "role": {
                    "id": 4,
                    "name": "Company owner (approved)",
                    "description": "After company approved, tariff needed",
                    "type": "company_owner_approved",
                    "permissions": [
                      {
                        "id": 56,
                        "action": "api::company.company.my"
                      },
                      {
                        "id": 71,
                        "action": "plugin::users-permissions.user.me"
                      }
                    ]
                  },
                  "company": {
                    "id": 7,
                    "Name": "Example LLC",
                    "YRGPO": "",
                    "validation": "yes",
                    "tarificationDate": "2026-02-01",
                    "last_success_tarification": null,
                    "missed_calls_timing": {
                      "redTiming": 1800,
                      "greenTiming": 60,
                      "yellowTiming": 300
                    },
                    "demo_untill": null,
                    "credit_limit": 5,
                    "use_credit_for_service_payment": false,
                    "balance_modified_date": "2026-02-01T10:00:00.000Z",
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "updatedAt": "2026-02-01T10:00:00.000Z"
                  },
                  "sip": {
                    "id": 15,
                    "number": "001",
                    "password": "********",
                    "record_call": true,
                    "call_forwarding": "off",
                    "redirectActivity": null,
                    "redirectTo": null,
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "updatedAt": "2026-01-15T10:00:00.000Z",
                    "domain": {
                      "id": 3,
                      "name": "examplellc",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/users/me\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/users/me\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/users/me\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user/getCountryByIp": {
      "get": {
        "operationId": "authGetCountryByIp",
        "tags": [
          "Auth"
        ],
        "summary": "Geolocate the caller's IP",
        "description": "Returns the GeoIP record for the client IP (taken from the `X-Real-IP` header set by the proxy).\nUsed by the apps to pre-select the country / currency before registration. Does not require a token.\nReturns `null` if the IP is not found in the GeoIP database.\n\n\n**Required role:** Public, Employee, Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "responses": {
          "200": {
            "description": "GeoIP record (geoip-lite format) or null.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthGeoIp"
                },
                "example": {
                  "range": [
                    1834458112,
                    1834459135
                  ],
                  "country": "UA",
                  "region": "30",
                  "eu": "0",
                  "timezone": "Europe/Kyiv",
                  "city": "Kyiv",
                  "ll": [
                    50.4547,
                    30.5238
                  ],
                  "metro": 0,
                  "area": 20
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/user/getCountryByIp\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user/getCountryByIp\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user/getCountryByIp\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/getBalance": {
      "get": {
        "operationId": "getCompanyBalance",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Current balance",
        "description": "Returns the current balance of the company and its credit limit. The value is read from the billing core\nand cached for 30 seconds, so a charge may appear with a short delay. `limit` is the amount the balance\nmay go below zero (credit limit); `0` means no credit. Amounts are in the company currency (see `GET /companies/my`).\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Balance and credit limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceInfo"
                },
                "example": {
                  "balance": 99.98,
                  "limit": 5
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/getBalance\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/getBalance\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/getBalance\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payments": {
      "get": {
        "operationId": "listPayments",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Payment history",
        "description": "Strapi core list of payments of the current company (the company filter is added automatically).\nTechnical child payments (internal transfers linked to a parent payment) are hidden.\n`amount < 0` is a charge from the balance, `amount > 0` is a credit (top-up, refund).\nUseful filters: `filters[status][$eq]=approved`, `filters[createdAt][$gte]=2026-01-01`,\n`filters[order][id][$eq]=42`, `filters[subscription][id][$notNull]=true`.\nUseful populate: `populate[payment_system][fields][0]=name`, `populate[currency][fields][0]=name_short`,\n`populate[order][fields][0]=orderId`, `populate[subscription]=true`.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of payments.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Payments.",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 101,
                      "attributes": {
                        "status": "approved",
                        "amount": -5.99,
                        "commentary": "Payment: 1790910007661138",
                        "payment_result": {
                          "id": 191703,
                          "status": 200,
                          "message": "OK"
                        },
                        "createdAt": "2026-09-01T03:00:12.110Z",
                        "updatedAt": "2026-09-01T03:00:12.536Z",
                        "payment_system": {
                          "data": {
                            "id": 2,
                            "attributes": {
                              "name": "Balance"
                            }
                          }
                        },
                        "currency": {
                          "data": {
                            "id": 1,
                            "attributes": {
                              "name_short": "USD"
                            }
                          }
                        },
                        "order": {
                          "data": {
                            "id": 42,
                            "attributes": {
                              "orderId": "1788231620358546"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 4,
                      "total": 87
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payments\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payments\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payments\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payments/{id}": {
      "get": {
        "operationId": "getPayment",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Payment by id",
        "description": "Returns one payment. Use `populate` to include `payment_system`, `currency`, `order`, `subscription`.\nOnly payments of your own company should be requested; take ids from `GET /payments` or the finance feeds.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Payment id.",
            "schema": {
              "type": "integer",
              "example": 101
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Payment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Payment"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "status": "declined",
                      "amount": -5.99,
                      "commentary": "Payment: 1790910007661138",
                      "payment_result": {
                        "Result": "Not enough money on balance"
                      },
                      "createdAt": "2026-10-02T03:00:07.661Z",
                      "updatedAt": "2026-10-02T03:00:07.835Z",
                      "payment_system": {
                        "data": {
                          "id": 2,
                          "attributes": {
                            "name": "Balance"
                          }
                        }
                      },
                      "currency": {
                        "data": {
                          "id": 1,
                          "attributes": {
                            "name_short": "USD"
                          }
                        }
                      },
                      "order": {
                        "data": null
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payments/101\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payments/101\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payments/101\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-acts": {
      "get": {
        "operationId": "listPaymentActs",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Acts (monthly statements)",
        "description": "Returns the acts of the current company (the company filter is added automatically). An act is a monthly\nstatement: list of charged services and per-number call costs for a period. `meta.totals` holds the sum of\n`amount` over the whole filtered set (not only the current page) in the company currency.\nUseful filters: `filters[createdAt][$gte]=2026-01-01`, `sort=createdAt:desc`.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of acts with totals.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Acts.",
                      "items": {
                        "$ref": "#/components/schemas/PaymentAct"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaymentActListMeta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 15,
                      "attributes": {
                        "name": "Акт рахунок за Квітень 2026",
                        "amount": 2.26,
                        "body": {
                          "data": {
                            "date": {
                              "from": "2026-04-01",
                              "to": "2026-04-30"
                            },
                            "dataJSON": {
                              "total": 2.26,
                              "refill": 0,
                              "tariff": [],
                              "phone": [],
                              "domain": [],
                              "esim": [],
                              "recall_widget": [],
                              "callback_widget": [],
                              "sip-trunk": [],
                              "payment": [],
                              "phone_calls": [
                                [
                                  "380441234567",
                                  339,
                                  2.26
                                ]
                              ]
                            }
                          }
                        },
                        "createdAt": "2026-05-01T05:00:04.431Z",
                        "updatedAt": "2026-05-01T05:00:04.431Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 3
                    },
                    "totals": {
                      "amount": 8.46,
                      "currency": "UAH"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payment-acts\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-acts\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-acts\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-acts/{id}": {
      "get": {
        "operationId": "getPaymentAct",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Act by id",
        "description": "Returns one act of the current company. Acts of other companies are not found (404-like empty `data`).\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Act id.",
            "schema": {
              "type": "integer",
              "example": 15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Act. `data` is null if the act does not belong to the company.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PaymentAct"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 15,
                    "attributes": {
                      "name": "Акт рахунок за Квітень 2026",
                      "amount": 2.26,
                      "body": {
                        "data": {
                          "date": {
                            "from": "2026-04-01",
                            "to": "2026-04-30"
                          },
                          "dataJSON": {
                            "total": 2.26,
                            "refill": 0,
                            "tariff": [],
                            "phone": [],
                            "domain": [],
                            "esim": [],
                            "recall_widget": [],
                            "callback_widget": [],
                            "sip-trunk": [],
                            "payment": [],
                            "phone_calls": [
                              [
                                "380441234567",
                                339,
                                2.26
                              ]
                            ]
                          }
                        }
                      },
                      "createdAt": "2026-05-01T05:00:04.431Z",
                      "updatedAt": "2026-05-01T05:00:04.431Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payment-acts/15\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-acts/15\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-acts/15\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-acts/{id}/printPdf": {
      "get": {
        "operationId": "printPaymentActPdf",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Act as PDF",
        "description": "Renders the act as a PDF document (Ukrainian template) and returns the file. Available only for companies\nwhose currency is UAH and whose owner is registered as a legal entity / sole proprietor (not a physical person).\nThe document includes the opening/closing balance for the period read from the billing core.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Act id.",
            "schema": {
              "type": "integer",
              "example": 15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF file.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "PDF document."
                }
              }
            }
          },
          "400": {
            "description": "Not available for the company (currency is not UAH or the owner is a physical person).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notUah": {
                    "summary": "Currency is not UAH",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Unavailable for company currency is not UAH !",
                        "details": {}
                      }
                    }
                  },
                  "physicalPerson": {
                    "summary": "Physical person",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Unavailable for physical person !",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payment-acts/15/printPdf\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-acts/15/printPdf\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-acts/15/printPdf\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-acts/act": {
      "get": {
        "operationId": "getPaymentActPreview",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Act preview for a period",
        "description": "Builds the act data for an arbitrary period on the fly without saving anything: charges from the balance for\nthe period grouped by service (numbers, tariff, domains, widgets, SIP trunks, eSIM, other payments) and\nper-number call costs (`phone_calls`: `[number, seconds, cost]`). The call part is fetched from the billing\ncore week by week, so long periods respond slowly (tens of seconds).\nDo not pass the `company` query parameter — it is ignored for authenticated users and breaks the request.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "date_from",
            "in": "query",
            "required": true,
            "description": "Period start, `YYYY-MM-DD`.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-09-01"
            }
          },
          {
            "name": "date_to",
            "in": "query",
            "required": true,
            "description": "Period end, `YYYY-MM-DD` (inclusive).",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-09-30"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Act data for the period.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Wrapper.",
                      "properties": {
                        "attributes": {
                          "type": "object",
                          "description": "Act content.",
                          "properties": {
                            "data": {
                              "$ref": "#/components/schemas/PaymentActBody"
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "attributes": {
                      "data": {
                        "date": {
                          "from": "2026-09-01",
                          "to": "2026-09-30"
                        },
                        "dataJSON": {
                          "total": 8.25,
                          "refill": 0,
                          "tariff": [],
                          "phone": [
                            {
                              "id": 7,
                              "fullnumber": "380441234567",
                              "calculatedMonthPrice": 5.99
                            }
                          ],
                          "domain": [],
                          "esim": [],
                          "recall_widget": [],
                          "callback_widget": [],
                          "sip-trunk": [],
                          "payment": [],
                          "phone_calls": [
                            [
                              "380441234567",
                              339,
                              2.26
                            ]
                          ]
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payment-acts/act?date_from=2026-09-01&date_to=2026-09-30\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-acts/act?date_from=2026-09-01&date_to=2026-09-30\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-acts/act?date_from=2026-09-01&date_to=2026-09-30\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-acts/generateAct": {
      "get": {
        "operationId": "generatePaymentAct",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Generate and save an act",
        "description": "**The `company` query parameter is ignored: the act is always generated for your own company.**\n\nBuilds the act for the period (same data as `GET /payment-acts/act`) and **saves it** as a new act record\nnamed after the month of `date_from` (e.g. \"Акт рахунок за Вересень 2026\"). The saved act is then available\nin `GET /payment-acts` and as PDF. Despite the GET method this call creates data; call it once per period.\nThere is no duplicate check: a repeated call for the same period creates a second act.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Creates an act record for the company; a repeated call for the same period creates a duplicate. Queries the billing core for call costs.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "company",
            "in": "query",
            "required": true,
            "deprecated": true,
            "description": "\\\"Ignored. The act is always generated for the authenticated user's company.\\\"",
            "schema": {
              "type": "integer",
              "example": 7
            }
          },
          {
            "name": "date_from",
            "in": "query",
            "required": true,
            "description": "Period start, `YYYY-MM-DD`. The act is named after this month.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-09-01"
            }
          },
          {
            "name": "date_to",
            "in": "query",
            "required": true,
            "description": "Period end, `YYYY-MM-DD` (inclusive).",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-09-30"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Created act (flat object, not a Strapi envelope).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentActFlat"
                },
                "example": {
                  "id": 16,
                  "name": "Акт рахунок за Вересень 2026",
                  "amount": 8.25,
                  "body": {
                    "data": {
                      "date": {
                        "from": "2026-09-01",
                        "to": "2026-09-30"
                      },
                      "dataJSON": {
                        "total": 8.25,
                        "refill": 0,
                        "tariff": [],
                        "phone": [
                          {
                            "id": 7,
                            "fullnumber": "380441234567",
                            "calculatedMonthPrice": 5.99
                          }
                        ],
                        "domain": [],
                        "esim": [],
                        "recall_widget": [],
                        "callback_widget": [],
                        "sip-trunk": [],
                        "payment": [],
                        "phone_calls": [
                          [
                            "380441234567",
                            339,
                            2.26
                          ]
                        ]
                      }
                    }
                  },
                  "createdAt": "2026-10-01T05:00:04.431Z",
                  "updatedAt": "2026-10-01T05:00:04.431Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payment-acts/generateAct?company=7&date_from=2026-09-01&date_to=2026-09-30\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-acts/generateAct?company=7&date_from=2026-09-01&date_to=2026-09-30\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-acts/generateAct?company=7&date_from=2026-09-01&date_to=2026-09-30\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-system/stripe/payment-sheet": {
      "post": {
        "operationId": "createStripePaymentSheet",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Stripe PaymentSheet for an order (mobile)",
        "description": "For mobile apps (Stripe PaymentSheet / Apple Pay / Google Pay). Creates a Stripe PaymentIntent for an order\ncreated with `POST /orders` whose payment system is Stripe (orders created with `paymentType: \"intent\"`).\nThe amount is converted from the order currency to USD cents. The card is saved to the Stripe customer for\nfuture off-session auto-renewals. The order becomes `approved` after the Stripe webhook confirms the payment.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** Creates a Stripe customer (if absent) and a PaymentIntent for the order total with the card saved for future charges; creates a payment record with status `new`. Money is charged when the client confirms the PaymentSheet.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Payload (a flat `{ \"orderId\": ... }` body is also accepted).",
                    "required": [
                      "orderId"
                    ],
                    "properties": {
                      "orderId": {
                        "type": "string",
                        "description": "Order number `orderId` from `POST /orders`.",
                        "example": "1788231620358546"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "orderId": "1788231620358546"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "PaymentSheet parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StripePaymentSheet"
                },
                "example": {
                  "paymentIntent": "pi_3Q********_secret_********",
                  "ephemeralKey": "ek_test_********",
                  "customer": "cus_********",
                  "publishableKey": "pk_live_********",
                  "amount": 599,
                  "currency": "usd",
                  "recurring": {
                    "intervalUnit": "month",
                    "intervalCount": 1,
                    "amount": 5.99,
                    "currency": "usd",
                    "label": "номер +15551234567",
                    "description": "Ежемесячное продление: номер +15551234567",
                    "managementUrl": "https://cabinet.example.com/call-processing-numbers"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "orderIdRequired": {
                    "summary": "orderId missing",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "BadRequestError",
                        "message": "orderId is required",
                        "details": {}
                      }
                    }
                  },
                  "notOwn": {
                    "summary": "Order of another company",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Order does not belong to company",
                        "details": {}
                      }
                    }
                  },
                  "notStripe": {
                    "summary": "Order is not a Stripe order",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Order payment system is not stripe",
                        "details": {}
                      }
                    }
                  },
                  "noRate": {
                    "summary": "No exchange rate",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Stripe: нет валидного курса для UAH",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Order not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Order not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/payment-system/stripe/payment-sheet\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"orderId\": \"1788231620358546\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-sheet\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"orderId\": \"1788231620358546\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-sheet\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"orderId\":\"1788231620358546\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-system/stripe/payment-method": {
      "get": {
        "operationId": "getStripeDefaultCard",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Saved card for auto-renewal",
        "description": "Returns the card that will be charged for auto-renewals: the default Stripe payment method of the company,\nor the first saved card if no default is set. `{ \"hasCard\": false }` when nothing is saved.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default card or `hasCard: false`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StripeDefaultCard"
                },
                "examples": {
                  "card": {
                    "summary": "Card saved",
                    "value": {
                      "hasCard": true,
                      "brand": "visa",
                      "last4": "4242",
                      "expMonth": 12,
                      "expYear": 2028,
                      "isDefault": true,
                      "isExpired": false
                    }
                  },
                  "none": {
                    "summary": "No card",
                    "value": {
                      "hasCard": false
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payment-system/stripe/payment-method\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-method\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-method\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-system/stripe/payment-methods": {
      "get": {
        "operationId": "listStripeCards",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Saved cards",
        "description": "Lists all cards saved to the company's Stripe customer with the default and expired flags. Duplicate entries of the same physical card are merged on the fly.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Detaches duplicate card entries in Stripe (same card saved twice).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Cards.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cards": {
                      "type": "array",
                      "description": "Saved cards (empty if no Stripe customer).",
                      "items": {
                        "$ref": "#/components/schemas/StripeCard"
                      }
                    }
                  }
                },
                "example": {
                  "cards": [
                    {
                      "id": "pm_********",
                      "brand": "visa",
                      "last4": "4242",
                      "expMonth": 12,
                      "expYear": 2028,
                      "isDefault": true,
                      "isExpired": false
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-system/stripe/payment-methods/{id}/default": {
      "put": {
        "operationId": "setStripeDefaultCard",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Make a card default",
        "description": "Sets the card as the default payment method; subsequent auto-renewals are charged to it. The card must belong to the company's Stripe customer.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Changes the default payment method in Stripe.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Stripe payment method id (`pm_...`).",
            "schema": {
              "type": "string",
              "example": "pm_1Q********"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Always true.",
                      "example": true
                    },
                    "defaultPaymentMethod": {
                      "type": "string",
                      "description": "New default payment method id.",
                      "example": "pm_1Q********"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "defaultPaymentMethod": "pm_1Q********"
                }
              }
            }
          },
          "400": {
            "description": "Card does not belong to the company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Payment method does not belong to this company",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The company has no Stripe customer (no saved cards).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Stripe customer not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods/pm_1Q********/default\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods/pm_1Q********/default\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods/pm_1Q********/default\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/payment-system/stripe/payment-methods/{id}": {
      "delete": {
        "operationId": "deleteStripeCard",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Remove a saved card",
        "description": "Detaches the card from the company's Stripe customer. The last remaining card cannot be removed — to stop\ncharges, disable auto-renewal of the subscription instead (`PUT /subscriptions/update_state/{subscriptionId}`).\nIf the default card is removed, the first remaining card becomes default.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Permanently detaches the card in Stripe; may change the default card.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Stripe payment method id (`pm_...`).",
            "schema": {
              "type": "string",
              "example": "pm_1Q********"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Always true.",
                      "example": true
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Card does not belong to the company or it is the last card.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notOwn": {
                    "summary": "Not own card",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Payment method does not belong to this company",
                        "details": {}
                      }
                    }
                  },
                  "lastCard": {
                    "summary": "Last card",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Нельзя удалить последнюю карту. Останови автосписание тумблером автопродления.",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The company has no Stripe customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Stripe customer not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods/pm_1Q********\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods/pm_1Q********\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/payment-system/stripe/payment-methods/pm_1Q********\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/currencies": {
      "get": {
        "operationId": "listCurrencies",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Currencies",
        "description": "Reference list of currencies used by the platform (USD, EUR, UAH). The company currency is fixed at registration; order and price amounts are in that currency.\n\n**Required role:** Company owner (approved), Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          }
        ],
        "responses": {
          "200": {
            "description": "Currencies.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Currencies.",
                      "items": {
                        "$ref": "#/components/schemas/Currency"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "attributes": {
                        "name_short": "USD",
                        "name_long": "US Dollar",
                        "symbol": "$",
                        "prefix": "",
                        "suffix": "",
                        "createdAt": "2024-04-23T20:18:29.106Z",
                        "updatedAt": "2024-04-23T20:18:29.106Z"
                      }
                    },
                    {
                      "id": 3,
                      "attributes": {
                        "name_short": "UAH",
                        "name_long": "Гривня",
                        "symbol": "₴",
                        "prefix": "",
                        "suffix": "",
                        "createdAt": "2024-04-23T20:18:29.169Z",
                        "updatedAt": "2024-05-30T11:45:17.449Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 3
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/currencies\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/currencies\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/currencies\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/currencies/{id}": {
      "get": {
        "operationId": "getCurrency",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Currency by id",
        "description": "Returns one currency.\n\n**Required role:** Company owner (approved), Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Currency id.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Currency.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Currency"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "attributes": {
                      "name_short": "USD",
                      "name_long": "US Dollar",
                      "symbol": "$",
                      "prefix": "",
                      "suffix": "",
                      "createdAt": "2024-04-23T20:18:29.106Z",
                      "updatedAt": "2024-04-23T20:18:29.106Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/currencies/1\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/currencies/1\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/currencies/1\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/tariffs": {
      "get": {
        "operationId": "listTariffs",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Tariffs",
        "description": "Catalogue of tariffs (plans). Use `populate[tariff_restrictions]=true` to get the limits (max numbers,\nemployees, domains, widgets) and `populate[prices][populate][price_by_field]=true` to get prices per currency\n(`price` — monthly fee, `domain_cost`, `recall_widget_cost`, `callback_widget_cost`, `per_trunk_price`,\n`sms_incoming_price`, `sms_outgoing_price`). Avoid `populate=*`: it also loads unrelated relations.\nThe company's current tariff is in `GET /companies/my`.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Tariffs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Tariffs.",
                      "items": {
                        "$ref": "#/components/schemas/Tariff"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "attributes": {
                        "Name": "Basic",
                        "custom": false,
                        "isAi": false,
                        "createdAt": "2024-02-16T14:04:57.675Z",
                        "updatedAt": "2026-05-06T09:24:06.362Z",
                        "tariff_restrictions": {
                          "data": [
                            {
                              "id": 1,
                              "attributes": {
                                "maxPhones": 4,
                                "maxEmployees": 10,
                                "maxDomains": 3,
                                "maxIVRSubVoutines": 1,
                                "max_callback_widgets": 1,
                                "maxRecallWidgets": 1
                              }
                            }
                          ]
                        },
                        "prices": {
                          "data": [
                            {
                              "id": 3,
                              "attributes": {
                                "price_by_field": [
                                  {
                                    "id": 11,
                                    "name": "price",
                                    "price": 0,
                                    "isPriceStatic": false
                                  },
                                  {
                                    "id": 12,
                                    "name": "domain_cost",
                                    "price": 1,
                                    "isPriceStatic": false
                                  }
                                ]
                              }
                            }
                          ]
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 5
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/tariffs\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/tariffs\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/tariffs\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/tariffs/{id}": {
      "get": {
        "operationId": "getTariff",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Tariff by id",
        "description": "Returns one tariff. Same `populate` options as in the list.\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Tariff id.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Tariff.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Tariff"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "attributes": {
                      "Name": "Basic",
                      "custom": false,
                      "isAi": false,
                      "createdAt": "2024-02-16T14:04:57.675Z",
                      "updatedAt": "2026-05-06T09:24:06.362Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/tariffs/1\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/tariffs/1\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/tariffs/1\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/tariff-restrictions": {
      "get": {
        "operationId": "listTariffRestrictions",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Tariff limits",
        "description": "Limits attached to tariffs: maximum numbers, employees, domains, IVR sub-menus and widgets. Orders and employee creation are validated against the limits of the company tariff.\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          }
        ],
        "responses": {
          "200": {
            "description": "Limits.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Limit sets.",
                      "items": {
                        "$ref": "#/components/schemas/TariffRestriction"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "attributes": {
                        "maxPhones": 4,
                        "maxEmployees": 10,
                        "maxDomains": 3,
                        "maxIVRSubVoutines": 1,
                        "max_callback_widgets": 1,
                        "maxRecallWidgets": 1,
                        "createdAt": "2023-01-30T13:41:14.528Z",
                        "updatedAt": "2024-08-15T10:27:28.972Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 5
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/tariff-restrictions\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/tariff-restrictions\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/tariff-restrictions\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/tariff-restrictions/{id}": {
      "get": {
        "operationId": "getTariffRestriction",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Tariff limits by id",
        "description": "Returns one set of limits.\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Limit set id.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Limits.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/TariffRestriction"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "attributes": {
                      "maxPhones": 4,
                      "maxEmployees": 10,
                      "maxDomains": 3,
                      "maxIVRSubVoutines": 1,
                      "max_callback_widgets": 1,
                      "maxRecallWidgets": 1,
                      "createdAt": "2023-01-30T13:41:14.528Z",
                      "updatedAt": "2024-08-15T10:27:28.972Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/tariff-restrictions/1\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/tariff-restrictions/1\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/tariff-restrictions/1\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/orders/expenses": {
      "get": {
        "operationId": "listExpenses",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Expenses feed",
        "description": "Unified list of the company's expenses: purchase orders (`source: order`, any status) plus charges from the\nbalance that have no order (`source: payment` — subscription auto-renewals, SMS, other charges).\nRows are sorted by date descending and paginated in memory. `meta.totals.amount` is the sum of **approved**\nrows over the whole period in the company currency. Period filter: `filters[createdAt][$between][0]=2026-09-01&filters[createdAt][$between][1]=2026-09-30`.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "pagination[page]",
            "in": "query",
            "required": false,
            "description": "Page number, from 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pagination[pageSize]",
            "in": "query",
            "required": false,
            "description": "Rows per page (default 10).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 10,
              "example": 10
            }
          },
          {
            "name": "filters[createdAt][$between]",
            "in": "query",
            "required": false,
            "description": "Period as two dates: `filters[createdAt][$between][0]=2026-09-01&filters[createdAt][$between][1]=2026-09-30`. Without it the whole history is returned.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "date"
              },
              "minItems": 2,
              "maxItems": 2,
              "example": [
                "2026-09-01",
                "2026-09-30"
              ]
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Expense rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FinanceFeedResponse"
                },
                "example": {
                  "data": [
                    {
                      "id": "o-42",
                      "attributes": {
                        "id": "o-42",
                        "refId": 42,
                        "source": "order",
                        "kind": "order",
                        "description": "Покупка номера",
                        "descriptionKey": "phone",
                        "descriptionParams": null,
                        "amount": 6.49,
                        "status": "approved",
                        "paymentMethod": "stripe",
                        "date": "2026-09-10T08:33:05.765Z",
                        "currency": "USD"
                      }
                    },
                    {
                      "id": "p-101",
                      "attributes": {
                        "id": "p-101",
                        "refId": 101,
                        "source": "payment",
                        "kind": "subscription",
                        "description": "Продление номера",
                        "descriptionKey": "renewPhone",
                        "descriptionParams": null,
                        "amount": 5.99,
                        "status": "approved",
                        "paymentMethod": "mc-billing",
                        "date": "2026-09-01T03:00:07.661Z",
                        "currency": "USD"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 3,
                      "total": 24
                    },
                    "totals": {
                      "amount": 118.4,
                      "currency": "USD"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/orders/expenses\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/orders/expenses\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/orders/expenses\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/orders/income": {
      "get": {
        "operationId": "listIncome",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Top-ups feed",
        "description": "Unified list of balance top-ups: top-up orders (`kind: topup`, any status) plus credits without an order\n(refunds, manual adjustments by support). Purchases paid by card are **not** income — they do not increase\nthe balance and are shown in `GET /orders/expenses`. Same query parameters and response format as the\nexpenses feed; `meta.totals.amount` sums approved rows.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "pagination[page]",
            "in": "query",
            "required": false,
            "description": "Page number, from 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pagination[pageSize]",
            "in": "query",
            "required": false,
            "description": "Rows per page (default 10).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 10,
              "example": 10
            }
          },
          {
            "name": "filters[createdAt][$between]",
            "in": "query",
            "required": false,
            "description": "Period as two dates: `filters[createdAt][$between][0]=2026-09-01&filters[createdAt][$between][1]=2026-09-30`. Without it the whole history is returned.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "date"
              },
              "minItems": 2,
              "maxItems": 2,
              "example": [
                "2026-09-01",
                "2026-09-30"
              ]
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Top-up rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FinanceFeedResponse"
                },
                "example": {
                  "data": [
                    {
                      "id": "o-40",
                      "attributes": {
                        "id": "o-40",
                        "refId": 40,
                        "source": "order",
                        "kind": "topup",
                        "description": "Пополнение баланса",
                        "descriptionKey": "topup",
                        "descriptionParams": null,
                        "amount": 30,
                        "status": "approved",
                        "paymentMethod": "wayforpay",
                        "date": "2026-01-15T08:33:05.765Z",
                        "currency": "USD"
                      }
                    },
                    {
                      "id": "p-77",
                      "attributes": {
                        "id": "p-77",
                        "refId": 77,
                        "source": "payment",
                        "kind": "manual",
                        "description": "Зачисление на баланс",
                        "descriptionKey": "balanceCredit",
                        "descriptionParams": null,
                        "amount": 10,
                        "status": "approved",
                        "paymentMethod": "mc-billing",
                        "date": "2026-01-10T12:00:00.000Z",
                        "currency": "USD"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 2,
                      "total": 19
                    },
                    "totals": {
                      "amount": 640,
                      "currency": "USD"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/orders/income\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/orders/income\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/orders/income\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/spentByData": {
      "get": {
        "operationId": "getSpentSince",
        "tags": [
          "Balance & Payments"
        ],
        "summary": "Spent since a date",
        "description": "Total of the company's charges from `from` (Unix seconds, start of that day) to the end of the current day:\nservice charges from the balance plus call costs (the same aggregation as the act preview). The call part is\nfetched from the billing core week by week, so long periods respond slowly. The sign follows how charges are\nrecorded in the act (service charges may be negative); treat the absolute value as the amount spent.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "description": "Period start as Unix timestamp in seconds.",
            "schema": {
              "type": "integer",
              "example": 1756684800
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Total.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Wrapper.",
                      "properties": {
                        "attributes": {
                          "type": "object",
                          "description": "Result.",
                          "properties": {
                            "result": {
                              "type": "number",
                              "description": "Total for the period in the company currency.",
                              "example": 12.35
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "attributes": {
                      "result": 12.35
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`from` is missing or not a number.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "`from` is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/statistics/spentByData?from=1756684800\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/spentByData?from=1756684800\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/spentByData?from=1756684800\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widgets": {
      "get": {
        "operationId": "listCallbackWidgets",
        "tags": [
          "Callback widget"
        ],
        "summary": "List callback widgets",
        "description": "Returns the callback widgets of the user's company (the list is filtered by the company of the authenticated user).\nA callback widget is a button on your website: the visitor leaves a phone number and the platform calls the visitor\nback and connects the call to the configured employee or group (`redirect`).\n\nUseful `populate` values: `callback_widget_design`, `callback_widget_activity`, `redirect` (with nested\n`populate[redirect][populate]=*` to get the user/group), `callback_widget_tickets`.\nFilters: `filters[activity][$eq]=true`, `filters[sitename][$contains]=example`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of callback widgets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Callback widgets.",
                      "items": {
                        "$ref": "#/components/schemas/CallbackWidget"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 15,
                      "attributes": {
                        "sitename": "example.com",
                        "activity": true,
                        "widget_name": "Main site",
                        "work_time": {
                          "mon": "09:00-18:00",
                          "tue": "09:00-18:00",
                          "wed": "09:00-18:00",
                          "thu": "09:00-18:00",
                          "fri": "09:00-18:00"
                        },
                        "active_on_exit": true,
                        "hide_on_weekend": false,
                        "widget_hash": "5c5822c3-0356-4230-b855-0879ff937e3d",
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-02-01T12:00:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widgets\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createCallbackWidget",
        "tags": [
          "Callback widget"
        ],
        "summary": "Create callback widget",
        "description": "Creates a callback widget for the user's company. The company is attached automatically.\n`widget_hash` (used in the embed code, see `GET /callback-widgets/getCode/{id}`) is generated by the server.\n\n`sitename` must be the bare domain of the site where the widget is installed (e.g. `example.com`, without protocol\nand `www.`): public ticket requests from the widget are accepted only if the `Origin` of the request matches it.\n`redirect` defines who receives the call: an employee (`user`) or a call group (`group`).\n\nAfter creation set up the look and behaviour with `POST /callback-widget-designs` and `POST /callback-widget-activities`\n(pass `callback_widget: <id>`), or pass an existing `callback_widget_activity` id in this request.\n\nTariff limit: each tariff includes a number of free callback widgets (`max_callback_widgets`). If the company already\nhas that many widgets, the new widget is paid: an order is created and the monthly price `callback_widget_cost`\nof the tariff is charged from the balance (a monthly subscription for the widget is created). If the balance is\ninsufficient, the request fails with 400 `Not enough money on balance` and a declined order is recorded.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** If the tariff limit of free callback widgets is exceeded: creates an order, charges the tariff price `callback_widget_cost` from the balance and starts a monthly subscription for the widget. Otherwise none.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetInput"
                  }
                }
              },
              "example": {
                "data": {
                  "sitename": "example.com",
                  "widget_name": "Main site",
                  "activity": true,
                  "redirect": {
                    "user": 7
                  },
                  "work_time": {
                    "mon": "09:00-18:00",
                    "tue": "09:00-18:00",
                    "wed": "09:00-18:00",
                    "thu": "09:00-18:00",
                    "fri": "09:00-18:00"
                  },
                  "active_on_exit": true,
                  "hide_on_weekend": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created widget.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidget"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 15,
                    "attributes": {
                      "sitename": "example.com",
                      "activity": true,
                      "widget_name": "Main site",
                      "work_time": {
                        "mon": "09:00-18:00",
                        "tue": "09:00-18:00",
                        "wed": "09:00-18:00",
                        "thu": "09:00-18:00",
                        "fri": "09:00-18:00"
                      },
                      "active_on_exit": true,
                      "hide_on_weekend": false,
                      "widget_hash": "5c5822c3-0356-4230-b855-0879ff937e3d",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: insufficient balance for a paid widget (`Not enough money on balance`), missing required field (`sitename must be defined.`, `widget_name must be defined.`), or `sitename` already used (`This attribute must be unique`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Not enough money on balance",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/callback-widgets\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"sitename\": \"example.com\",\n    \"widget_name\": \"Main site\",\n    \"activity\": true,\n    \"redirect\": {\n      \"user\": 7\n    },\n    \"work_time\": {\n      \"mon\": \"09:00-18:00\",\n      \"tue\": \"09:00-18:00\",\n      \"wed\": \"09:00-18:00\",\n      \"thu\": \"09:00-18:00\",\n      \"fri\": \"09:00-18:00\"\n    },\n    \"active_on_exit\": true,\n    \"hide_on_weekend\": false\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"sitename\": \"example.com\",\n      \"widget_name\": \"Main site\",\n      \"activity\": true,\n      \"redirect\": {\n        \"user\": 7\n      },\n      \"work_time\": {\n        \"mon\": \"09:00-18:00\",\n        \"tue\": \"09:00-18:00\",\n        \"wed\": \"09:00-18:00\",\n        \"thu\": \"09:00-18:00\",\n        \"fri\": \"09:00-18:00\"\n      },\n      \"active_on_exit\": true,\n      \"hide_on_weekend\": false\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"sitename\":\"example.com\",\"widget_name\":\"Main site\",\"activity\":true,\"redirect\":{\"user\":7},\"work_time\":{\"mon\":\"09:00-18:00\",\"tue\":\"09:00-18:00\",\"wed\":\"09:00-18:00\",\"thu\":\"09:00-18:00\",\"fri\":\"09:00-18:00\"},\"active_on_exit\":true,\"hide_on_weekend\":false}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widgets/{id}": {
      "put": {
        "operationId": "updateCallbackWidget",
        "tags": [
          "Callback widget"
        ],
        "summary": "Update callback widget",
        "description": "Updates the fields of a callback widget (Strapi envelope `{ \"data\": {...} }`, only the passed fields change).\nUse it to turn the widget on/off (`activity`), change the schedule (`work_time`, `hide_on_weekend`), the site domain\nor the call destination (`redirect`). `widget_hash` and `company` cannot be changed.\nPass the id of a widget of your company (see `GET /callback-widgets`).\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Callback widget id.",
            "schema": {
              "type": "integer",
              "example": 15
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetInput"
                  }
                }
              },
              "example": {
                "data": {
                  "activity": false,
                  "work_time": {
                    "mon": "10:00-19:00",
                    "tue": "10:00-19:00",
                    "wed": "10:00-19:00",
                    "thu": "10:00-19:00",
                    "fri": "10:00-19:00",
                    "sat": "10:00-15:00"
                  },
                  "hide_on_weekend": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated widget.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidget"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 15,
                    "attributes": {
                      "sitename": "example.com",
                      "activity": false,
                      "widget_name": "Main site",
                      "work_time": {
                        "mon": "10:00-19:00",
                        "tue": "10:00-19:00",
                        "wed": "10:00-19:00",
                        "thu": "10:00-19:00",
                        "fri": "10:00-19:00",
                        "sat": "10:00-15:00"
                      },
                      "active_on_exit": true,
                      "hide_on_weekend": true,
                      "widget_hash": "5c5822c3-0356-4230-b855-0879ff937e3d",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error (e.g. `sitename` already used by another widget: `This attribute must be unique`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "This attribute must be unique",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/callback-widgets/15\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"activity\": false,\n    \"work_time\": {\n      \"mon\": \"10:00-19:00\",\n      \"tue\": \"10:00-19:00\",\n      \"wed\": \"10:00-19:00\",\n      \"thu\": \"10:00-19:00\",\n      \"fri\": \"10:00-19:00\",\n      \"sat\": \"10:00-15:00\"\n    },\n    \"hide_on_weekend\": true\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets/15\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"activity\": false,\n      \"work_time\": {\n        \"mon\": \"10:00-19:00\",\n        \"tue\": \"10:00-19:00\",\n        \"wed\": \"10:00-19:00\",\n        \"thu\": \"10:00-19:00\",\n        \"fri\": \"10:00-19:00\",\n        \"sat\": \"10:00-15:00\"\n      },\n      \"hide_on_weekend\": true\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets/15\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"activity\":false,\"work_time\":{\"mon\":\"10:00-19:00\",\"tue\":\"10:00-19:00\",\"wed\":\"10:00-19:00\",\"thu\":\"10:00-19:00\",\"fri\":\"10:00-19:00\",\"sat\":\"10:00-15:00\"},\"hide_on_weekend\":true}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteCallbackWidget",
        "tags": [
          "Callback widget"
        ],
        "summary": "Delete callback widget",
        "description": "Deletes a callback widget permanently together with its design, activity settings, all its tickets and call\nstatistics records. The monthly subscription for the widget (if it was a paid one) is revoked. The embed code\non the site stops working. This cannot be undone.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the widget, its design, activity, tickets and statistics; revokes the widget subscription.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Callback widget id.",
            "schema": {
              "type": "integer",
              "example": 15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted widget.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidget"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 15,
                    "attributes": {
                      "sitename": "example.com",
                      "activity": false,
                      "widget_name": "Main site",
                      "work_time": {
                        "mon": "09:00-18:00"
                      },
                      "active_on_exit": true,
                      "hide_on_weekend": false,
                      "widget_hash": "5c5822c3-0356-4230-b855-0879ff937e3d",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/callback-widgets/15\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets/15\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets/15\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "callbackWidgetCallTo",
        "tags": [
          "Callback widget"
        ],
        "summary": "Start the callback call for a ticket",
        "description": "Service method of the callback engine: originates the callback call for an existing ticket right now.\nNormally you do not call it yourself: when a ticket is created (`POST /callback-widget-tickets`) the platform\nqueues this call automatically at `when_call_time`. Use it only to force an immediate (re)call for a ticket.\n\nThe call is placed from the SIP line of the employee (`redirect.user`) or from the call group (`redirect.group`)\nof the widget `data.widgetId` to the visitor number `data.contact_number`; the ticket `data.ticketId` is switched\nto status `calling`. The path parameter `{id}` is not used by the handler — the widget is taken from `data.widgetId`.\nRate limit: 20 requests per 5 minutes per IP.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Initiates an outgoing call from the company (employee SIP line or group) to the visitor number; the call is charged by the company tariff for outgoing calls. Sets the ticket status to `calling`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Callback widget id (ignored by the handler; pass the same id as `data.widgetId`).",
            "schema": {
              "type": "integer",
              "example": 15
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "widgetId",
                      "contact_number",
                      "ticketId"
                    ],
                    "description": "Call parameters.",
                    "properties": {
                      "widgetId": {
                        "type": "integer",
                        "description": "Callback widget id whose `redirect` (employee or group) places the call.",
                        "example": 15
                      },
                      "contact_number": {
                        "type": "string",
                        "description": "Visitor phone number to call.",
                        "example": "+380441234567"
                      },
                      "ticketId": {
                        "type": "integer",
                        "description": "Ticket id to switch to `calling`.",
                        "example": 101
                      },
                      "actionId": {
                        "type": "string",
                        "description": "Optional call action id for matching with call statistics; by default the ticket's `action_id`.",
                        "example": "1737000000"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "widgetId": 15,
                  "contact_number": "+380441234567",
                  "ticketId": 101,
                  "actionId": "1737000000"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Call originated. The body is the literal `true`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "boolean",
                  "description": "Always `true` on success."
                },
                "example": true
              }
            }
          },
          "400": {
            "description": "`data.contact_number` is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Contact_number must be defined",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "description": "Widget `data.widgetId` not found or has no `redirect` (user/group), or the telephony server is unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 500,
                    "name": "InternalServerError",
                    "message": "Internal Server Error",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/callback-widgets/15\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"widgetId\": 15,\n    \"contact_number\": \"+380441234567\",\n    \"ticketId\": 101,\n    \"actionId\": \"1737000000\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets/15\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"widgetId\": 15,\n      \"contact_number\": \"+380441234567\",\n      \"ticketId\": 101,\n      \"actionId\": \"1737000000\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets/15\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"widgetId\":15,\"contact_number\":\"+380441234567\",\"ticketId\":101,\"actionId\":\"1737000000\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widgets/byId/{id}": {
      "get": {
        "operationId": "getCallbackWidgetById",
        "tags": [
          "Callback widget"
        ],
        "summary": "Get callback widget by id",
        "description": "Returns one callback widget by its numeric id. Supports `populate`, e.g.\n`populate[redirect][populate]=*&populate[callback_widget_design][populate]=*&populate[callback_widget_activity]=true`.\n(The plain `GET /callback-widgets/{id}` route is reserved for the widget script and looks the widget up by `widget_hash`.)\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Callback widget id.",
            "schema": {
              "type": "integer",
              "example": 15
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Callback widget. If the id does not exist, `data` is `null`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidget"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 15,
                    "attributes": {
                      "sitename": "example.com",
                      "activity": true,
                      "widget_name": "Main site",
                      "work_time": {
                        "mon": "09:00-18:00",
                        "tue": "09:00-18:00",
                        "wed": "09:00-18:00",
                        "thu": "09:00-18:00",
                        "fri": "09:00-18:00"
                      },
                      "active_on_exit": true,
                      "hide_on_weekend": false,
                      "widget_hash": "5c5822c3-0356-4230-b855-0879ff937e3d",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z",
                      "redirect": {
                        "id": 3,
                        "call_end": null,
                        "extension": null,
                        "user": {
                          "data": {
                            "id": 7,
                            "attributes": {
                              "username": "+380441234567",
                              "email": "john.doe@example.com",
                              "firstName": "John",
                              "lastName": "Doe"
                            }
                          }
                        },
                        "group": {
                          "data": null
                        },
                        "ivr": {
                          "data": null
                        }
                      },
                      "callback_widget_design": {
                        "data": {
                          "id": 29,
                          "attributes": {
                            "button": "default",
                            "button_size": "medium",
                            "button_animation": true,
                            "button_color": "#3538CD",
                            "block_color": "#F2F2F2"
                          }
                        }
                      },
                      "callback_widget_activity": {
                        "data": {
                          "id": 29,
                          "attributes": {
                            "countdown": 30,
                            "after_open": 5,
                            "limit": 3
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widgets/byId/15\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets/byId/15\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets/byId/15\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widgets/firstTimeOrder/{id}": {
      "get": {
        "operationId": "getCallbackWidgetFirstTimeContacts",
        "tags": [
          "Callback widget"
        ],
        "summary": "First-time callers of a widget for a period",
        "description": "Report for a callback widget: unique phone numbers that left tickets in the period `from`–`to`, with the date\nof the first ticket of each number in that period. Use it to count new leads from the widget.\nBoth `from` and `to` are required (dates/timestamps understood by the database, e.g. `2026-01-01` or\n`2026-01-01T00:00:00.000Z`); without them the request fails with 500.\nThe response is a plain array (no Strapi `data`/`meta` envelope).\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Callback widget id.",
            "schema": {
              "type": "integer",
              "example": 15
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "description": "Period start (inclusive), ISO date or date-time.",
            "schema": {
              "type": "string",
              "example": "2026-01-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "description": "Period end (inclusive), ISO date or date-time.",
            "schema": {
              "type": "string",
              "example": "2026-02-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Unique numbers with the date of their first ticket in the period.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "description": "One item per unique phone number.",
                  "items": {
                    "$ref": "#/components/schemas/CallbackWidgetFirstTimeContact"
                  }
                },
                "example": [
                  {
                    "contact_number": "+380441234567",
                    "first_created_at": "2026-01-05T09:12:48.546Z"
                  },
                  {
                    "contact_number": "+15551234567",
                    "first_created_at": "2026-01-20T14:10:00.673Z"
                  }
                ]
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "description": "`from`/`to` missing or not a valid date.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 500,
                    "name": "InternalServerError",
                    "message": "Internal Server Error",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widgets/firstTimeOrder/15?from=2026-01-01&to=2026-02-01\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets/firstTimeOrder/15?from=2026-01-01&to=2026-02-01\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets/firstTimeOrder/15?from=2026-01-01&to=2026-02-01\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widgets/getCode/{id}": {
      "get": {
        "operationId": "getCallbackWidgetEmbedCode",
        "tags": [
          "Callback widget"
        ],
        "summary": "Embed code of a callback widget",
        "description": "Returns the HTML/JS snippet to paste into your website before `</body>`. The snippet loads the widget script\nand styles from the OneVOIPlanet widget CDN and renders the widget into `<div id='app-onevoip-widgets'></div>`.\n`{id}` is the widget `widget_hash` (UUID from the widget attributes), not the numeric id. The value is not\nvalidated: the snippet is generated for any string, so copy `widget_hash` exactly.\nThe response is `text/html`, not JSON.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Widget `widget_hash` (UUID).",
            "schema": {
              "type": "string",
              "example": "5c5822c3-0356-4230-b855-0879ff937e3d"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML snippet with the widget loader script.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "HTML: `<script>` loader plus `<div id='app-onevoip-widgets'></div>`."
                },
                "example": "<script>\n    (function (d, w, s) {\n        var ss = document.createElement('link'), sf = document.createElement('link'),\n            widgetHash = '5c5822c3-0356-4230-b855-0879ff937e3d',\n            gcw = d.createElement(s), sn = d.getElementsByTagName(s)[0];\n        ss.rel = 'preload stylesheet'; ss.as = 'style';\n        ss.href = '//widgets.onevoiplanet.ua/css/style.css?' + Date.now();\n        document.getElementsByTagName('head')[0].appendChild(ss);\n        sf.rel = 'stylesheet'; sf.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700&display=swap';\n        document.getElementsByTagName('head')[0].appendChild(sf);\n        gcw.async = true;\n        gcw.src = '//widgets.onevoiplanet.ua/widget-callback/' + widgetHash + '?' + Date.now();\n        gcw.setAttribute('data-id', widgetHash); gcw.setAttribute('data-name', 'WidgetCallBack');\n        sn.parentNode.insertBefore(gcw, sn);\n    })(document, window, 'script');\n</script>\n<div id='app-onevoip-widgets'></div>\n"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widgets/getCode/5c5822c3-0356-4230-b855-0879ff937e3d\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widgets/getCode/5c5822c3-0356-4230-b855-0879ff937e3d\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widgets/getCode/5c5822c3-0356-4230-b855-0879ff937e3d\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widget-tickets": {
      "get": {
        "operationId": "listCallbackWidgetTickets",
        "tags": [
          "Callback widget"
        ],
        "summary": "List callback tickets",
        "description": "Returns callback tickets (requests left by site visitors). Always filter by your widget:\n`filters[callback_widget][id][$eq]=15` (or `filters[callback_widget][company][id][$eq]=<companyId>`), and use\n`sort=createdAt:desc`. Useful filters: `filters[ticket_status][$eq]=queued`, `filters[isFirstTime][$eq]=true`,\n`filters[createdAt][$gte]=2026-01-01`. `populate=callback_widget` adds the widget, `populate=statistics` the call\nlegs of the callback (see Calls & Statistics).\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of tickets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Tickets.",
                      "items": {
                        "$ref": "#/components/schemas/CallbackWidgetTicket"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 101,
                      "attributes": {
                        "contact_number": "+380441234567",
                        "when_call_time": "2026-01-20T14:10:00.000Z",
                        "ticket_status": "call_success",
                        "action_id": "1737000000",
                        "isFirstTime": true,
                        "domain": "https://example.com/pricing",
                        "ip": "203.0.113.10",
                        "createdAt": "2026-01-20T14:10:00.673Z",
                        "updatedAt": "2026-01-20T14:15:19.138Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widget-tickets\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-tickets\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-tickets\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createCallbackWidgetTicket",
        "tags": [
          "Callback widget"
        ],
        "summary": "Create callback ticket (public, triggers the callback)",
        "description": "Public method called by the widget script on your website when a visitor asks to be called back. No token is\nrequired. Creating a ticket starts the callback: the platform queues a call from the widget's employee/group to\n`contact_number` at `when_call_time` (immediately if omitted or in the past) and sends an in-app/push\nnotification about the new request to the responsible employee(s).\n\nChecks performed, in order:\n* `data.callback_widget` is required (400 `Callback_widget must be defined`);\n* per-IP limit: the number of tickets with status `queued` from the same IP for this widget must be below\n  `limit` of the widget activity settings (400 `IP limit!`);\n* the `Origin` header domain must equal the widget `sitename` (400 ``Your sitename isn`t match the URL domain!``);\n  the browser sends `Origin` automatically; without the header the request fails with 500;\n* if the number is in the company's contacts with `allowlist`/`global_allowlist` set (blocked), 403\n  `Your phone is on the black list`.\n`ticket_status`, `action_id` and `statistic` in the body are ignored; `ip` is taken from the request; `isFirstTime`\nis set to `true` when this number has never left a ticket for this widget. Rate limit: 20 requests per 5 minutes per IP.\n\n\n**Required role:** Public, Company owner (approved), Supervisor\n\n**Side effects:** Initiates a callback call from the company number/SIP line (employee or group of the widget) to the visitor at `when_call_time`; the call is charged by the company tariff. Sends notifications to the responsible employee(s).",
        "security": [],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Origin of the page with the widget; its domain must equal the widget `sitename` (sent by browsers automatically).",
            "schema": {
              "type": "string",
              "example": "https://example.com"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "callback_widget",
                      "contact_number"
                    ],
                    "description": "Ticket fields (Strapi envelope).",
                    "properties": {
                      "callback_widget": {
                        "type": "integer",
                        "description": "Callback widget id.",
                        "example": 15
                      },
                      "contact_number": {
                        "type": "string",
                        "minLength": 9,
                        "maxLength": 15,
                        "description": "Visitor phone number to call back (9–15 characters).",
                        "example": "+380441234567"
                      },
                      "when_call_time": {
                        "type": "string",
                        "format": "date-time",
                        "description": "When to call (ISO-8601). Omit or pass now for an immediate callback.",
                        "example": "2026-01-20T14:10:00.000Z"
                      },
                      "domain": {
                        "type": "string",
                        "description": "URL of the page where the request was made (for reporting).",
                        "example": "https://example.com/pricing"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "callback_widget": 15,
                  "contact_number": "+380441234567",
                  "when_call_time": "2026-01-20T14:10:00.000Z",
                  "domain": "https://example.com/pricing"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created ticket (status `queued`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetTicket"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "contact_number": "+380441234567",
                      "when_call_time": "2026-01-20T14:10:00.000Z",
                      "ticket_status": "queued",
                      "action_id": "1737000000",
                      "isFirstTime": true,
                      "domain": "https://example.com/pricing",
                      "ip": "203.0.113.10",
                      "createdAt": "2026-01-20T14:10:00.673Z",
                      "updatedAt": "2026-01-20T14:10:00.673Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `Callback_widget must be defined`, `IP limit!`, ``Your sitename isn`t match the URL domain!``, or `contact_number` missing/too short (`contact_number must be at least 9 characters`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "IP limit!",
                    "details": {}
                  }
                }
              }
            }
          },
          "403": {
            "description": "The phone number is blocked in the company contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Your phone is on the black list",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "description": "`Origin` header missing, or widget `callback_widget` not found / has no activity settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 500,
                    "name": "InternalServerError",
                    "message": "Internal Server Error",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/callback-widget-tickets\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"callback_widget\": 15,\n    \"contact_number\": \"+380441234567\",\n    \"when_call_time\": \"2026-01-20T14:10:00.000Z\",\n    \"domain\": \"https://example.com/pricing\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-tickets\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"callback_widget\": 15,\n      \"contact_number\": \"+380441234567\",\n      \"when_call_time\": \"2026-01-20T14:10:00.000Z\",\n      \"domain\": \"https://example.com/pricing\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-tickets\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"callback_widget\":15,\"contact_number\":\"+380441234567\",\"when_call_time\":\"2026-01-20T14:10:00.000Z\",\"domain\":\"https://example.com/pricing\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widget-tickets/status": {
      "post": {
        "operationId": "refreshCallbackWidgetTicketStatus",
        "tags": [
          "Callback widget"
        ],
        "summary": "Recalculate ticket status from call result",
        "description": "Service method of the callback engine: recomputes `ticket_status` of a ticket from its latest call statistics\nrecord (the last call leg) and saves it. Normally the telephony backend calls it when the callback ends; call it\nyourself only to refresh a status that looks stale.\nRules: leg to the visitor (`rightLeg`) answered (SIP 200 / `Ok` / `Normal call clearing`) → `call_success`;\n`487 Request Terminated` → `canceled`; other `rightLeg` result → `call_success_left` (employee answered, visitor\nnot reached); leg to the employee (`leftLeg`) failed → `call_unsuccess`; otherwise `calling`.\nReturns the updated ticket as a flat object (no `data`/`attributes` envelope). Rate limit: 20 requests per 5 minutes per IP.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Updates `ticket_status` of the ticket.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "id"
                    ],
                    "description": "Ticket reference.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Ticket id.",
                        "example": 101
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "id": 101
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated ticket (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallbackWidgetTicketAttributes"
                },
                "example": {
                  "id": 101,
                  "contact_number": "+380441234567",
                  "when_call_time": "2026-01-20T14:10:00.000Z",
                  "ticket_status": "call_success",
                  "action_id": "1737000000",
                  "isFirstTime": true,
                  "domain": "https://example.com/pricing",
                  "ip": "203.0.113.10",
                  "createdAt": "2026-01-20T14:10:00.673Z",
                  "updatedAt": "2026-01-20T14:15:19.138Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "description": "Ticket not found or it has no call statistics yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 500,
                    "name": "InternalServerError",
                    "message": "Internal Server Error",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/callback-widget-tickets/status\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"id\": 101\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-tickets/status\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"id\": 101\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-tickets/status\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"id\":101}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widget-tickets/{id}": {
      "get": {
        "operationId": "getCallbackWidgetTicket",
        "tags": [
          "Callback widget"
        ],
        "summary": "Get callback ticket",
        "description": "Returns one ticket by id. `populate=callback_widget` adds the widget, `populate=statistics` the call legs.\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Ticket id.",
            "schema": {
              "type": "integer",
              "example": 101
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Ticket.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetTicket"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "contact_number": "+380441234567",
                      "when_call_time": "2026-01-20T14:10:00.000Z",
                      "ticket_status": "call_success",
                      "action_id": "1737000000",
                      "isFirstTime": true,
                      "domain": "https://example.com/pricing",
                      "ip": "203.0.113.10",
                      "createdAt": "2026-01-20T14:10:00.673Z",
                      "updatedAt": "2026-01-20T14:15:19.138Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widget-tickets/101\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-tickets/101\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-tickets/101\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateCallbackWidgetTicket",
        "tags": [
          "Callback widget"
        ],
        "summary": "Update callback ticket",
        "description": "Updates ticket fields (Strapi envelope). Typical use: cancel a queued request (`ticket_status: canceled`) or\ncorrect `when_call_time`/`contact_number`. Note: changing `when_call_time` does not reschedule a call that is\nalready queued.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Ticket id.",
            "schema": {
              "type": "integer",
              "example": 101
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Fields to update.",
                    "properties": {
                      "ticket_status": {
                        "type": "string",
                        "enum": [
                          "queued",
                          "calling",
                          "call_success",
                          "call_success_left",
                          "canceled",
                          "call_unsuccess"
                        ],
                        "description": "New status.",
                        "example": "canceled"
                      },
                      "when_call_time": {
                        "type": "string",
                        "format": "date-time",
                        "description": "Requested call time.",
                        "example": "2026-01-21T10:00:00.000Z"
                      },
                      "contact_number": {
                        "type": "string",
                        "minLength": 9,
                        "maxLength": 15,
                        "description": "Visitor phone number.",
                        "example": "+380441234567"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "ticket_status": "canceled"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated ticket.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetTicket"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "contact_number": "+380441234567",
                      "when_call_time": "2026-01-20T14:10:00.000Z",
                      "ticket_status": "canceled",
                      "action_id": "1737000000",
                      "isFirstTime": true,
                      "domain": "https://example.com/pricing",
                      "ip": "203.0.113.10",
                      "createdAt": "2026-01-20T14:10:00.673Z",
                      "updatedAt": "2026-01-20T14:20:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/callback-widget-tickets/101\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"ticket_status\": \"canceled\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-tickets/101\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"ticket_status\": \"canceled\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-tickets/101\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"ticket_status\":\"canceled\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteCallbackWidgetTicket",
        "tags": [
          "Callback widget"
        ],
        "summary": "Delete callback ticket",
        "description": "Deletes a ticket permanently. Call statistics records linked to it are kept. Deleting a `queued` ticket does not cancel an already queued call.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the ticket.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Ticket id.",
            "schema": {
              "type": "integer",
              "example": 101
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted ticket.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetTicket"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "contact_number": "+380441234567",
                      "when_call_time": "2026-01-20T14:10:00.000Z",
                      "ticket_status": "canceled",
                      "action_id": "1737000000",
                      "isFirstTime": true,
                      "domain": "https://example.com/pricing",
                      "ip": "203.0.113.10",
                      "createdAt": "2026-01-20T14:10:00.673Z",
                      "updatedAt": "2026-01-20T14:20:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/callback-widget-tickets/101\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-tickets/101\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-tickets/101\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widget-activities": {
      "get": {
        "operationId": "listCallbackWidgetActivities",
        "tags": [
          "Callback widget"
        ],
        "summary": "List widget activity settings",
        "description": "Returns activity (behaviour) settings of callback widgets: countdown before the call, auto-open delay and the\nper-IP request limit. One record belongs to one widget. Filter by widget: `filters[callback_widget][id][$eq]=15`;\n`populate=callback_widget` adds the widget.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of activity settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Activity settings.",
                      "items": {
                        "$ref": "#/components/schemas/CallbackWidgetActivity"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 29,
                      "attributes": {
                        "countdown": 30,
                        "after_open": 5,
                        "limit": 3,
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widget-activities\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-activities\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-activities\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createCallbackWidgetActivity",
        "tags": [
          "Callback widget"
        ],
        "summary": "Create widget activity settings",
        "description": "Creates activity settings and links them to a callback widget (`callback_widget`). Each widget needs exactly one record: without it public ticket creation fails. All three numeric fields are required.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetActivityInput"
                  }
                }
              },
              "example": {
                "data": {
                  "countdown": 30,
                  "after_open": 5,
                  "limit": 3,
                  "callback_widget": 15
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetActivity"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "countdown": 30,
                      "after_open": 5,
                      "limit": 3,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: required field missing (`countdown must be defined.`) or value above the maximum (`countdown must be less than or equal to 300`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "countdown must be defined.",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/callback-widget-activities\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"countdown\": 30,\n    \"after_open\": 5,\n    \"limit\": 3,\n    \"callback_widget\": 15\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-activities\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"countdown\": 30,\n      \"after_open\": 5,\n      \"limit\": 3,\n      \"callback_widget\": 15\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-activities\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"countdown\":30,\"after_open\":5,\"limit\":3,\"callback_widget\":15}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widget-activities/{id}": {
      "get": {
        "operationId": "getCallbackWidgetActivity",
        "tags": [
          "Callback widget"
        ],
        "summary": "Get widget activity settings",
        "description": "Returns one activity settings record by id. `populate=callback_widget` adds the widget.\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Activity settings id.",
            "schema": {
              "type": "integer",
              "example": 29
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Activity settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetActivity"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "countdown": 30,
                      "after_open": 5,
                      "limit": 3,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widget-activities/29\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-activities/29\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-activities/29\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateCallbackWidgetActivity",
        "tags": [
          "Callback widget"
        ],
        "summary": "Update widget activity settings",
        "description": "Updates activity settings (Strapi envelope, only passed fields change). Changes apply to the widget on the site on its next load.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Activity settings id.",
            "schema": {
              "type": "integer",
              "example": 29
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetActivityInput"
                  }
                }
              },
              "example": {
                "data": {
                  "countdown": 25,
                  "limit": 5
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetActivity"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "countdown": 25,
                      "after_open": 5,
                      "limit": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/callback-widget-activities/29\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"countdown\": 25,\n    \"limit\": 5\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-activities/29\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"countdown\": 25,\n      \"limit\": 5\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-activities/29\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"countdown\":25,\"limit\":5}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteCallbackWidgetActivity",
        "tags": [
          "Callback widget"
        ],
        "summary": "Delete widget activity settings",
        "description": "Deletes the activity settings record permanently. The widget is left without behaviour settings and stops accepting tickets until new settings are created.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the settings record.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Activity settings id.",
            "schema": {
              "type": "integer",
              "example": 29
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetActivity"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "countdown": 25,
                      "after_open": 5,
                      "limit": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/callback-widget-activities/29\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-activities/29\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-activities/29\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widget-designs": {
      "get": {
        "operationId": "listCallbackWidgetDesigns",
        "tags": [
          "Callback widget"
        ],
        "summary": "List widget designs",
        "description": "Returns design settings of callback widgets: button size/colours, position and the texts shown in the form.\nOne record belongs to one widget. Filter by widget: `filters[callback_widget][id][$eq]=15`.\nUse `populate=*` (or `populate[0]=location&populate[1]=inscription`) to get the `location` and `inscription` components.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of designs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Designs.",
                      "items": {
                        "$ref": "#/components/schemas/CallbackWidgetDesign"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 29,
                      "attributes": {
                        "button": "default",
                        "button_size": "medium",
                        "button_animation": true,
                        "button_color": "#3538CD",
                        "block_color": "#F2F2F2",
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z",
                        "location": {
                          "id": 1,
                          "corner_location": "right",
                          "padding_x": 10,
                          "padding_y": 10
                        },
                        "inscription": [
                          {
                            "id": 1,
                            "title": "Have a question? We will call you back!",
                            "text": "We will call you in %countdown% seconds. It is fast and free!",
                            "button_text": "Call me",
                            "phone_placeholder": "Enter your phone number",
                            "call_later_label": "Not a good time to talk?",
                            "blocked_country": "Sorry, we cannot call %country% or the number is incorrect.",
                            "call_limit": "You can order another callback in half an hour.",
                            "make_order": "Request accepted. We are calling you now!",
                            "call_now": null,
                            "thanks": null
                          }
                        ]
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widget-designs\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-designs\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-designs\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createCallbackWidgetDesign",
        "tags": [
          "Callback widget"
        ],
        "summary": "Create widget design",
        "description": "Creates design settings and links them to a callback widget (`callback_widget`). Each widget needs one design.\n`inscription` is a list of text sets used by the widget form in different situations (e.g. main form, exit-intent\nform, out-of-hours form); texts may contain the placeholders `%countdown%` (seconds) and `%country%`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetDesignInput"
                  }
                }
              },
              "example": {
                "data": {
                  "callback_widget": 15,
                  "button": "default",
                  "button_size": "medium",
                  "button_animation": true,
                  "button_color": "#3538CD",
                  "block_color": "#F2F2F2",
                  "location": {
                    "corner_location": "right",
                    "padding_x": 10,
                    "padding_y": 10
                  },
                  "inscription": [
                    {
                      "title": "Have a question? We will call you back!",
                      "text": "We will call you in %countdown% seconds. It is fast and free!",
                      "button_text": "Call me",
                      "phone_placeholder": "Enter your phone number",
                      "call_later_label": "Not a good time to talk?",
                      "blocked_country": "Sorry, we cannot call %country% or the number is incorrect.",
                      "call_limit": "You can order another callback in half an hour.",
                      "make_order": "Request accepted. We are calling you now!"
                    },
                    {
                      "title": "Did not find what you were looking for?",
                      "text": "We will call you in %countdown% seconds.",
                      "button_text": "Call me",
                      "phone_placeholder": "Enter your phone number",
                      "call_later_label": "Not a good time to talk?",
                      "blocked_country": "Sorry, we cannot call %country% or the number is incorrect.",
                      "call_limit": "You can order another callback in half an hour.",
                      "make_order": "Request accepted. We are calling you now!"
                    },
                    {
                      "title": "We are closed right now",
                      "text": "Let us call you back",
                      "button_text": "Call me",
                      "phone_placeholder": "Enter your phone number",
                      "blocked_country": "Sorry, we cannot call %country% or the number is incorrect.",
                      "call_limit": "You can order another callback in half an hour.",
                      "make_order": "Request accepted. We will call you at the selected time."
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created design.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetDesign"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "button": "default",
                      "button_size": "medium",
                      "button_animation": true,
                      "button_color": "#3538CD",
                      "block_color": "#F2F2F2",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: required field missing (`button_size must be defined.`), invalid enum value (`button_size must be one of the following values: big, medium`) or text longer than allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "button_size must be defined.",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/callback-widget-designs\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"callback_widget\": 15,\n    \"button\": \"default\",\n    \"button_size\": \"medium\",\n    \"button_animation\": true,\n    \"button_color\": \"#3538CD\",\n    \"block_color\": \"#F2F2F2\",\n    \"location\": {\n      \"corner_location\": \"right\",\n      \"padding_x\": 10,\n      \"padding_y\": 10\n    },\n    \"inscription\": [\n      {\n        \"title\": \"Have a question? We will call you back!\",\n        \"text\": \"We will call you in %countdown% seconds. It is fast and free!\",\n        \"button_text\": \"Call me\",\n        \"phone_placeholder\": \"Enter your phone number\",\n        \"call_later_label\": \"Not a good time to talk?\",\n        \"blocked_country\": \"Sorry, we cannot call %country% or the number is incorrect.\",\n        \"call_limit\": \"You can order another callback in half an hour.\",\n        \"make_order\": \"Request accepted. We are calling you now!\"\n      },\n      {\n        \"title\": \"Did not find what you were looking for?\",\n        \"text\": \"We will call you in %countdown% seconds.\",\n        \"button_text\": \"Call me\",\n        \"phone_placeholder\": \"Enter your phone number\",\n        \"call_later_label\": \"Not a good time to talk?\",\n        \"blocked_country\": \"Sorry, we cannot call %country% or the number is incorrect.\",\n        \"call_limit\": \"You can order another callback in half an hour.\",\n        \"make_order\": \"Request accepted. We are calling you now!\"\n      },\n      {\n        \"title\": \"We are closed right now\",\n        \"text\": \"Let us call you back\",\n        \"button_text\": \"Call me\",\n        \"phone_placeholder\": \"Enter your phone number\",\n        \"blocked_country\": \"Sorry, we cannot call %country% or the number is incorrect.\",\n        \"call_limit\": \"You can order another callback in half an hour.\",\n        \"make_order\": \"Request accepted. We will call you at the selected time.\"\n      }\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-designs\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"callback_widget\": 15,\n      \"button\": \"default\",\n      \"button_size\": \"medium\",\n      \"button_animation\": true,\n      \"button_color\": \"#3538CD\",\n      \"block_color\": \"#F2F2F2\",\n      \"location\": {\n        \"corner_location\": \"right\",\n        \"padding_x\": 10,\n        \"padding_y\": 10\n      },\n      \"inscription\": [\n        {\n          \"title\": \"Have a question? We will call you back!\",\n          \"text\": \"We will call you in %countdown% seconds. It is fast and free!\",\n          \"button_text\": \"Call me\",\n          \"phone_placeholder\": \"Enter your phone number\",\n          \"call_later_label\": \"Not a good time to talk?\",\n          \"blocked_country\": \"Sorry, we cannot call %country% or the number is incorrect.\",\n          \"call_limit\": \"You can order another callback in half an hour.\",\n          \"make_order\": \"Request accepted. We are calling you now!\"\n        },\n        {\n          \"title\": \"Did not find what you were looking for?\",\n          \"text\": \"We will call you in %countdown% seconds.\",\n          \"button_text\": \"Call me\",\n          \"phone_placeholder\": \"Enter your phone number\",\n          \"call_later_label\": \"Not a good time to talk?\",\n          \"blocked_country\": \"Sorry, we cannot call %country% or the number is incorrect.\",\n          \"call_limit\": \"You can order another callback in half an hour.\",\n          \"make_order\": \"Request accepted. We are calling you now!\"\n        },\n        {\n          \"title\": \"We are closed right now\",\n          \"text\": \"Let us call you back\",\n          \"button_text\": \"Call me\",\n          \"phone_placeholder\": \"Enter your phone number\",\n          \"blocked_country\": \"Sorry, we cannot call %country% or the number is incorrect.\",\n          \"call_limit\": \"You can order another callback in half an hour.\",\n          \"make_order\": \"Request accepted. We will call you at the selected time.\"\n        }\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-designs\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"callback_widget\":15,\"button\":\"default\",\"button_size\":\"medium\",\"button_animation\":true,\"button_color\":\"#3538CD\",\"block_color\":\"#F2F2F2\",\"location\":{\"corner_location\":\"right\",\"padding_x\":10,\"padding_y\":10},\"inscription\":[{\"title\":\"Have a question? We will call you back!\",\"text\":\"We will call you in %countdown% seconds. It is fast and free!\",\"button_text\":\"Call me\",\"phone_placeholder\":\"Enter your phone number\",\"call_later_label\":\"Not a good time to talk?\",\"blocked_country\":\"Sorry, we cannot call %country% or the number is incorrect.\",\"call_limit\":\"You can order another callback in half an hour.\",\"make_order\":\"Request accepted. We are calling you now!\"},{\"title\":\"Did not find what you were looking for?\",\"text\":\"We will call you in %countdown% seconds.\",\"button_text\":\"Call me\",\"phone_placeholder\":\"Enter your phone number\",\"call_later_label\":\"Not a good time to talk?\",\"blocked_country\":\"Sorry, we cannot call %country% or the number is incorrect.\",\"call_limit\":\"You can order another callback in half an hour.\",\"make_order\":\"Request accepted. We are calling you now!\"},{\"title\":\"We are closed right now\",\"text\":\"Let us call you back\",\"button_text\":\"Call me\",\"phone_placeholder\":\"Enter your phone number\",\"blocked_country\":\"Sorry, we cannot call %country% or the number is incorrect.\",\"call_limit\":\"You can order another callback in half an hour.\",\"make_order\":\"Request accepted. We will call you at the selected time.\"}]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/callback-widget-designs/{id}": {
      "get": {
        "operationId": "getCallbackWidgetDesign",
        "tags": [
          "Callback widget"
        ],
        "summary": "Get widget design",
        "description": "Returns one design by id. Use `populate=*` to include `location`, `inscription` and the widget.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Design id.",
            "schema": {
              "type": "integer",
              "example": 29
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Design.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetDesign"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "button": "default",
                      "button_size": "medium",
                      "button_animation": true,
                      "button_color": "#3538CD",
                      "block_color": "#F2F2F2",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z",
                      "location": {
                        "id": 1,
                        "corner_location": "right",
                        "padding_x": 10,
                        "padding_y": 10
                      },
                      "inscription": [
                        {
                          "id": 1,
                          "title": "Have a question? We will call you back!",
                          "text": "We will call you in %countdown% seconds. It is fast and free!",
                          "button_text": "Call me",
                          "phone_placeholder": "Enter your phone number",
                          "call_later_label": "Not a good time to talk?",
                          "blocked_country": "Sorry, we cannot call %country% or the number is incorrect.",
                          "call_limit": "You can order another callback in half an hour.",
                          "make_order": "Request accepted. We are calling you now!",
                          "call_now": null,
                          "thanks": null
                        }
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/callback-widget-designs/29\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-designs/29\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-designs/29\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateCallbackWidgetDesign",
        "tags": [
          "Callback widget"
        ],
        "summary": "Update widget design",
        "description": "Updates design settings (Strapi envelope, only passed fields change). When passing `inscription`, send the whole list: components are replaced, not merged.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Design id.",
            "schema": {
              "type": "integer",
              "example": 29
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetDesignInput"
                  }
                }
              },
              "example": {
                "data": {
                  "button_size": "big",
                  "button_color": "#4671F6",
                  "location": {
                    "corner_location": "left",
                    "padding_x": 20,
                    "padding_y": 20
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated design.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetDesign"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "button": "default",
                      "button_size": "big",
                      "button_animation": true,
                      "button_color": "#4671F6",
                      "block_color": "#F2F2F2",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/callback-widget-designs/29\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"button_size\": \"big\",\n    \"button_color\": \"#4671F6\",\n    \"location\": {\n      \"corner_location\": \"left\",\n      \"padding_x\": 20,\n      \"padding_y\": 20\n    }\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-designs/29\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"button_size\": \"big\",\n      \"button_color\": \"#4671F6\",\n      \"location\": {\n        \"corner_location\": \"left\",\n        \"padding_x\": 20,\n        \"padding_y\": 20\n      }\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-designs/29\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"button_size\":\"big\",\"button_color\":\"#4671F6\",\"location\":{\"corner_location\":\"left\",\"padding_x\":20,\"padding_y\":20}}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteCallbackWidgetDesign",
        "tags": [
          "Callback widget"
        ],
        "summary": "Delete widget design",
        "description": "Deletes the design record permanently. The widget is left without design settings until a new one is created.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the design record.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Design id.",
            "schema": {
              "type": "integer",
              "example": 29
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted design.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetDesign"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 29,
                    "attributes": {
                      "button": "default",
                      "button_size": "big",
                      "button_animation": true,
                      "button_color": "#4671F6",
                      "block_color": "#F2F2F2",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/callback-widget-designs/29\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/callback-widget-designs/29\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/callback-widget-designs/29\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/recall-widgets": {
      "get": {
        "operationId": "listRecallWidgets",
        "tags": [
          "Callback widget"
        ],
        "summary": "List recall widgets",
        "description": "Returns the recall (\"call from site\") widgets of the user's company (filtered by the user's company).\nUnlike a callback widget, a recall widget lets the visitor call the company directly from the browser\n(WebRTC): the platform creates a dedicated SIP line for the widget and routes the call to the configured employee,\ngroup or IVR (`redirect_data`). The widget can also show Telegram/Viber links.\nUseful `populate`: `redirect_data` (with `populate[redirect_data][populate]=*`), `color_state`, `telegram`, `viber`, `sip`.\nFilters: `filters[active][$eq]=true`.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of recall widgets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Recall widgets.",
                      "items": {
                        "$ref": "#/components/schemas/CallbackWidgetRecall"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 4,
                      "attributes": {
                        "name": "Call us",
                        "active": true,
                        "text": "Call us from the site",
                        "shape": "circle",
                        "font": "Inter",
                        "uuid": "0f8fad5b-d9cb-469f-a165-70867728950e",
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/recall-widgets\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/recall-widgets\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/recall-widgets\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createRecallWidget",
        "tags": [
          "Callback widget"
        ],
        "summary": "Create recall widget",
        "description": "Creates a recall (\"call from site\") widget for the user's company. The company is attached automatically and a\n`uuid` for the embed code is generated. On creation the platform also creates a dedicated SIP line for the widget\nin the SIP domain `domain` (the visitor's browser call is placed through it) and registers it on the telephony\nservers. `domain` is required on create and cannot be changed later.\n\nValidation (in order): if `telegram.display`/`viber.display` is `true`, the corresponding `nickName` is required;\n`color_state` must contain exactly 4 items (button colour states); `redirect_data` must point to a `user`, `group`\nor `ivr`; `domain` is required.\n\nTariff limit: the tariff includes `maxRecallWidgets` free recall widgets. Above that the new widget is paid: an\norder is created and the monthly tariff price `recall_widget_cost` is charged from the balance (monthly\nsubscription). If the balance is insufficient, 400 `Not enough money on balance` and a declined order is recorded.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Creates a SIP line for the widget in the given SIP domain and registers it on the telephony servers. If the tariff limit of free recall widgets is exceeded: creates an order, charges `recall_widget_cost` from the balance and starts a monthly subscription.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetRecallInput"
                  }
                }
              },
              "example": {
                "data": {
                  "name": "Call us",
                  "active": true,
                  "domain": 3,
                  "redirect_data": {
                    "group": 2
                  },
                  "telegram": {
                    "display": true,
                    "nickName": "example_support"
                  },
                  "viber": {
                    "display": false,
                    "nickName": ""
                  },
                  "text": "Call us from the site",
                  "shape": "circle",
                  "font": "Inter",
                  "color_state": [
                    {
                      "text_color": "#FFFFFF",
                      "background_color": "#3538CD"
                    },
                    {
                      "text_color": "#FFFFFF",
                      "background_color": "#2A2DAB"
                    },
                    {
                      "text_color": "#FFFFFF",
                      "background_color": "#1F2280"
                    },
                    {
                      "text_color": "#9A9A9A",
                      "background_color": "#E5E5E5"
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created recall widget.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetRecall"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 4,
                    "attributes": {
                      "name": "Call us",
                      "active": true,
                      "text": "Call us from the site",
                      "shape": "circle",
                      "font": "Inter",
                      "uuid": "0f8fad5b-d9cb-469f-a165-70867728950e",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `Telegram nickname must be defined`, `Viber nickname must be defined`, `All color states must be defined`, `Relation to must be defined`, `Domain must be defined`, `Not enough money on balance`, or a schema error (`name must be defined.`, `name must be at least 3 characters`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "All color states must be defined",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/recall-widgets\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"name\": \"Call us\",\n    \"active\": true,\n    \"domain\": 3,\n    \"redirect_data\": {\n      \"group\": 2\n    },\n    \"telegram\": {\n      \"display\": true,\n      \"nickName\": \"example_support\"\n    },\n    \"viber\": {\n      \"display\": false,\n      \"nickName\": \"\"\n    },\n    \"text\": \"Call us from the site\",\n    \"shape\": \"circle\",\n    \"font\": \"Inter\",\n    \"color_state\": [\n      {\n        \"text_color\": \"#FFFFFF\",\n        \"background_color\": \"#3538CD\"\n      },\n      {\n        \"text_color\": \"#FFFFFF\",\n        \"background_color\": \"#2A2DAB\"\n      },\n      {\n        \"text_color\": \"#FFFFFF\",\n        \"background_color\": \"#1F2280\"\n      },\n      {\n        \"text_color\": \"#9A9A9A\",\n        \"background_color\": \"#E5E5E5\"\n      }\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/recall-widgets\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"name\": \"Call us\",\n      \"active\": true,\n      \"domain\": 3,\n      \"redirect_data\": {\n        \"group\": 2\n      },\n      \"telegram\": {\n        \"display\": true,\n        \"nickName\": \"example_support\"\n      },\n      \"viber\": {\n        \"display\": false,\n        \"nickName\": \"\"\n      },\n      \"text\": \"Call us from the site\",\n      \"shape\": \"circle\",\n      \"font\": \"Inter\",\n      \"color_state\": [\n        {\n          \"text_color\": \"#FFFFFF\",\n          \"background_color\": \"#3538CD\"\n        },\n        {\n          \"text_color\": \"#FFFFFF\",\n          \"background_color\": \"#2A2DAB\"\n        },\n        {\n          \"text_color\": \"#FFFFFF\",\n          \"background_color\": \"#1F2280\"\n        },\n        {\n          \"text_color\": \"#9A9A9A\",\n          \"background_color\": \"#E5E5E5\"\n        }\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/recall-widgets\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"name\":\"Call us\",\"active\":true,\"domain\":3,\"redirect_data\":{\"group\":2},\"telegram\":{\"display\":true,\"nickName\":\"example_support\"},\"viber\":{\"display\":false,\"nickName\":\"\"},\"text\":\"Call us from the site\",\"shape\":\"circle\",\"font\":\"Inter\",\"color_state\":[{\"text_color\":\"#FFFFFF\",\"background_color\":\"#3538CD\"},{\"text_color\":\"#FFFFFF\",\"background_color\":\"#2A2DAB\"},{\"text_color\":\"#FFFFFF\",\"background_color\":\"#1F2280\"},{\"text_color\":\"#9A9A9A\",\"background_color\":\"#E5E5E5\"}]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/recall-widgets/{id}": {
      "get": {
        "operationId": "getRecallWidget",
        "tags": [
          "Callback widget"
        ],
        "summary": "Get recall widget by UUID",
        "description": "Returns one recall widget. Note: `{id}` is the widget `uuid` (the value used in the embed code), not the numeric id.\nSupports `populate` (e.g. `populate=*`). If no widget has this uuid, the response is `200` with `data: null`.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Recall widget `uuid`.",
            "schema": {
              "type": "string",
              "example": "0f8fad5b-d9cb-469f-a165-70867728950e"
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Recall widget, or `data: null` if not found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetRecall"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 4,
                    "attributes": {
                      "name": "Call us",
                      "active": true,
                      "text": "Call us from the site",
                      "shape": "circle",
                      "font": "Inter",
                      "uuid": "0f8fad5b-d9cb-469f-a165-70867728950e",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z",
                      "redirect_data": {
                        "id": 5,
                        "call_end": null,
                        "extension": null
                      },
                      "telegram": {
                        "id": 5,
                        "nickName": "example_support",
                        "display": true
                      },
                      "viber": {
                        "id": 5,
                        "nickName": "",
                        "display": false
                      },
                      "color_state": [
                        {
                          "id": 1,
                          "text_color": "#FFFFFF",
                          "background_color": "#3538CD"
                        },
                        {
                          "id": 2,
                          "text_color": "#FFFFFF",
                          "background_color": "#2A2DAB"
                        },
                        {
                          "id": 3,
                          "text_color": "#FFFFFF",
                          "background_color": "#1F2280"
                        },
                        {
                          "id": 4,
                          "text_color": "#9A9A9A",
                          "background_color": "#E5E5E5"
                        }
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/recall-widgets/0f8fad5b-d9cb-469f-a165-70867728950e\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/recall-widgets/0f8fad5b-d9cb-469f-a165-70867728950e\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/recall-widgets/0f8fad5b-d9cb-469f-a165-70867728950e\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateRecallWidget",
        "tags": [
          "Callback widget"
        ],
        "summary": "Update recall widget",
        "description": "Updates recall widget fields (Strapi envelope, only passed fields change): name, `active`, texts, colours,\nmessenger links, `redirect_data`. `domain` and `company` in the body are ignored. `{id}` is the numeric id.\nValidation policies of create (4 colour states, messenger nicknames) are not applied on update, but the schema\nrules (`name` 3–50 chars, `text` up to 100, enum `shape`) are.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Recall widget id.",
            "schema": {
              "type": "integer",
              "example": 4
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/CallbackWidgetRecallInput"
                  }
                }
              },
              "example": {
                "data": {
                  "active": false,
                  "text": "Call us",
                  "redirect_data": {
                    "user": 7
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated recall widget.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetRecall"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 4,
                    "attributes": {
                      "name": "Call us",
                      "active": false,
                      "text": "Call us",
                      "shape": "circle",
                      "font": "Inter",
                      "uuid": "0f8fad5b-d9cb-469f-a165-70867728950e",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/recall-widgets/4\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"active\": false,\n    \"text\": \"Call us\",\n    \"redirect_data\": {\n      \"user\": 7\n    }\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/recall-widgets/4\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"active\": false,\n      \"text\": \"Call us\",\n      \"redirect_data\": {\n        \"user\": 7\n      }\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/recall-widgets/4\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"active\":false,\"text\":\"Call us\",\"redirect_data\":{\"user\":7}}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteRecallWidget",
        "tags": [
          "Callback widget"
        ],
        "summary": "Delete recall widget",
        "description": "Deletes a recall widget permanently together with its dedicated SIP line and call statistics records; the monthly subscription (if paid) is revoked. The embed code on the site stops working. Cannot be undone.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the widget, its SIP line and statistics; revokes the widget subscription.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Recall widget id.",
            "schema": {
              "type": "integer",
              "example": 4
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted recall widget.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallbackWidgetRecall"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 4,
                    "attributes": {
                      "name": "Call us",
                      "active": false,
                      "text": "Call us",
                      "shape": "circle",
                      "font": "Inter",
                      "uuid": "0f8fad5b-d9cb-469f-a165-70867728950e",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/recall-widgets/4\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/recall-widgets/4\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/recall-widgets/4\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/recall-widgets/getCode/{id}": {
      "get": {
        "operationId": "getRecallWidgetEmbedCode",
        "tags": [
          "Callback widget"
        ],
        "summary": "Embed code of a recall widget",
        "description": "Returns the HTML/JS snippet to paste into your website before `</body>`. The snippet loads the \"call from site\"\nwidget script and styles from the OneVOIPlanet widget CDN (with a 2-second delay) and renders it into\n`<div id='app-onevoip-widgets'></div>`. `{id}` is the widget `uuid`, not the numeric id; it is not validated,\nso copy `uuid` exactly. The response is `text/html`, not JSON.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Recall widget `uuid`.",
            "schema": {
              "type": "string",
              "example": "0f8fad5b-d9cb-469f-a165-70867728950e"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML snippet with the widget loader script.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "HTML: `<script>` loader plus `<div id='app-onevoip-widgets'></div>`."
                },
                "example": "<script>\n    (function (d, w, s) {\n        setTimeout(function () {\n            var ss = document.createElement('link'),\n                widgetHash = '0f8fad5b-d9cb-469f-a165-70867728950e',\n                gcw = d.createElement(s), sn = d.getElementsByTagName(s)[0];\n            ss.rel = 'preload stylesheet'; ss.as = 'style';\n            ss.href = '//widgets.onevoiplanet.ua/css/style.css?' + Date.now();\n            document.getElementsByTagName('head')[0].appendChild(ss);\n            gcw.async = true;\n            gcw.src = '//widgets.onevoiplanet.ua/widget-callsite/' + widgetHash + '?' + Date.now();\n            gcw.setAttribute('data-id', widgetHash); gcw.setAttribute('data-name', 'WidgetCallSite');\n            sn.parentNode.insertBefore(gcw, sn);\n        }, 2000);\n    })(document, window, 'script');\n</script>\n<div id='app-onevoip-widgets'></div>\n"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/recall-widgets/getCode/0f8fad5b-d9cb-469f-a165-70867728950e\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/recall-widgets/getCode/0f8fad5b-d9cb-469f-a165-70867728950e\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/recall-widgets/getCode/0f8fad5b-d9cb-469f-a165-70867728950e\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics": {
      "get": {
        "operationId": "listCalls",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Call history",
        "description": "Returns the call history of the company (one record per call; multi-leg calls — group, IVR, transfers — are grouped into one\nrecord with the legs in `callDetailInfo.subbranches`). The result is always limited to your company; **every role sees all company\ncalls here** — use `POST /statistics/myStat` to get the calls of the current employee only.\n\nUseful filters: `filters[callSetupTime][$gte]=<unix seconds>`, `filters[callDuration][$gte]=30`, `filters[code][$eq]=200` (answered),\n`filters[from][$contains]=38044`, `filters[sip][id][$eq]=12` (outgoing by employee), `filters[sipTo][id][$eq]=12` (incoming to employee).\nUseful `populate`: `sip`, `sipTo`, `conversation_sips`, `group`, `ivr`, `callRecord` (recording file), `contacts` (address-book matches by number),\n`recall_widget`, `callback_widget`, `missed_call`.\n\n**Call recordings:** recorded only when the employee's SIP line has `record_call` enabled and the call was answered (`code` 200).\n`callRecordStatus`: `compiling` → `ready` (file attached) or `disabled`. With `populate[callRecord]=true` the record carries `url`\n(relative, e.g. `/uploads/recordings/<file>.wav`); download it from `https://api.onevoiplanet.com<url>`.\nCall records older than 90 days are deleted automatically.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Calls.",
                      "items": {
                        "$ref": "#/components/schemas/CallsStatisticItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1001,
                      "attributes": {
                        "from": "380661234567",
                        "to": "380441234567",
                        "code": "200",
                        "reason": "Ok",
                        "callSide": "termination",
                        "callSetupTime": "1790931273",
                        "callStartTime": "1790931283",
                        "callEndTime": "1790931403",
                        "callDuration": 120,
                        "tarificatedDuration": 120,
                        "cost": 0,
                        "callCommentary": null,
                        "callEnder": false,
                        "isSystem": false,
                        "isFromGroup": true,
                        "isRecall": null,
                        "origin_call_id": null,
                        "originate_call_direction": null,
                        "callRecordStatus": "ready",
                        "callDetailInfo": {
                          "subbranches": []
                        },
                        "refered_to": null,
                        "redirectedFromSipToOutbound": false,
                        "ai_used": false,
                        "call_transcribation_data": null,
                        "createdAt": "2026-02-01T09:30:00.000Z",
                        "updatedAt": "2026-02-01T09:32:05.000Z",
                        "sipTo": {
                          "data": {
                            "id": 12,
                            "attributes": {
                              "number": "001"
                            }
                          }
                        },
                        "callRecord": {
                          "data": {
                            "id": 77,
                            "attributes": {
                              "name": "call-1001-mix.wav",
                              "ext": ".wav",
                              "mime": "audio/wave",
                              "size": 0.15,
                              "url": "/uploads/recordings/call-1001-mix.wav"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 4,
                      "total": 87
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/statistics\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/myStat": {
      "post": {
        "operationId": "myCalls",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "My calls (role-aware)",
        "description": "Same data as `GET /statistics`, but the query (filters, sort, pagination, populate) is sent in the JSON body under `params`\nand the visibility depends on the role: company owner and supervisor get all company calls; other roles (employee, finance)\nget only calls where their own SIP line took part (`sip`, `sipTo` or `conversation_sips`).\nUse it for a personal call journal. Returns an empty body if the user has no SIP line.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallsQueryBody"
              },
              "example": {
                "params": {
                  "filters": {
                    "callSetupTime": {
                      "$gte": 1790899200,
                      "$lte": 1790985599
                    }
                  },
                  "sort": [
                    "id:desc"
                  ],
                  "pagination": {
                    "page": 1,
                    "pageSize": 25
                  },
                  "populate": {
                    "sip": {
                      "fields": [
                        "number"
                      ]
                    },
                    "sipTo": {
                      "fields": [
                        "number"
                      ]
                    },
                    "callRecord": true,
                    "contacts": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Page of calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Calls.",
                      "items": {
                        "$ref": "#/components/schemas/CallsStatisticItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1001,
                      "attributes": {
                        "from": "380661234567",
                        "to": "380441234567",
                        "code": "200",
                        "reason": "Ok",
                        "callSide": "termination",
                        "callSetupTime": "1790931273",
                        "callStartTime": "1790931283",
                        "callEndTime": "1790931403",
                        "callDuration": 120,
                        "cost": 0,
                        "callRecordStatus": "ready",
                        "createdAt": "2026-02-01T09:30:00.000Z",
                        "updatedAt": "2026-02-01T09:32:05.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/statistics/myStat\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"params\": {\n    \"filters\": {\n      \"callSetupTime\": {\n        \"$gte\": 1790899200,\n        \"$lte\": 1790985599\n      }\n    },\n    \"sort\": [\n      \"id:desc\"\n    ],\n    \"pagination\": {\n      \"page\": 1,\n      \"pageSize\": 25\n    },\n    \"populate\": {\n      \"sip\": {\n        \"fields\": [\n          \"number\"\n        ]\n      },\n      \"sipTo\": {\n        \"fields\": [\n          \"number\"\n        ]\n      },\n      \"callRecord\": true,\n      \"contacts\": true\n    }\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/myStat\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"params\": {\n      \"filters\": {\n        \"callSetupTime\": {\n          \"$gte\": 1790899200,\n          \"$lte\": 1790985599\n        }\n      },\n      \"sort\": [\n        \"id:desc\"\n      ],\n      \"pagination\": {\n        \"page\": 1,\n        \"pageSize\": 25\n      },\n      \"populate\": {\n        \"sip\": {\n          \"fields\": [\n            \"number\"\n          ]\n        },\n        \"sipTo\": {\n          \"fields\": [\n            \"number\"\n          ]\n        },\n        \"callRecord\": true,\n        \"contacts\": true\n      }\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/myStat\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"params\":{\"filters\":{\"callSetupTime\":{\"$gte\":1790899200,\"$lte\":1790985599}},\"sort\":[\"id:desc\"],\"pagination\":{\"page\":1,\"pageSize\":25},\"populate\":{\"sip\":{\"fields\":[\"number\"]},\"sipTo\":{\"fields\":[\"number\"]},\"callRecord\":true,\"contacts\":true}}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/myStatToday": {
      "post": {
        "operationId": "myCallsToday",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "My calls for today",
        "description": "Same as `POST /statistics/myStat` with an extra condition `createdAt >= start of the current day` (server time, UTC).\nAny filters passed in `params.filters` are combined with it.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallsQueryBody"
              },
              "example": {
                "params": {
                  "sort": [
                    "id:desc"
                  ],
                  "pagination": {
                    "page": 1,
                    "pageSize": 25
                  },
                  "populate": {
                    "sip": {
                      "fields": [
                        "number"
                      ]
                    },
                    "sipTo": {
                      "fields": [
                        "number"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Page of today's calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Calls.",
                      "items": {
                        "$ref": "#/components/schemas/CallsStatisticItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1002,
                      "attributes": {
                        "from": "001domainexample",
                        "to": "380661234567",
                        "code": "487",
                        "reason": "Request Terminated",
                        "callSide": "origination",
                        "callSetupTime": "1790931273",
                        "callStartTime": "0",
                        "callEndTime": "1790931283",
                        "callDuration": 0,
                        "cost": 0,
                        "callRecordStatus": "disabled",
                        "createdAt": "2026-02-01T09:30:00.000Z",
                        "updatedAt": "2026-02-01T09:30:10.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/statistics/myStatToday\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"params\": {\n    \"sort\": [\n      \"id:desc\"\n    ],\n    \"pagination\": {\n      \"page\": 1,\n      \"pageSize\": 25\n    },\n    \"populate\": {\n      \"sip\": {\n        \"fields\": [\n          \"number\"\n        ]\n      },\n      \"sipTo\": {\n        \"fields\": [\n          \"number\"\n        ]\n      }\n    }\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/myStatToday\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"params\": {\n      \"sort\": [\n        \"id:desc\"\n      ],\n      \"pagination\": {\n        \"page\": 1,\n        \"pageSize\": 25\n      },\n      \"populate\": {\n        \"sip\": {\n          \"fields\": [\n            \"number\"\n          ]\n        },\n        \"sipTo\": {\n          \"fields\": [\n            \"number\"\n          ]\n        }\n      }\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/myStatToday\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"params\":{\"sort\":[\"id:desc\"],\"pagination\":{\"page\":1,\"pageSize\":25},\"populate\":{\"sip\":{\"fields\":[\"number\"]},\"sipTo\":{\"fields\":[\"number\"]}}}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/get-my-last-call": {
      "get": {
        "operationId": "getMyLastCall",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "My last call",
        "description": "Returns the most recent call in which the current user's SIP line was the caller or the callee, as a flat object\n(no `data/attributes` envelope). `fields[]` and `populate` query parameters limit/extend the returned fields,\ne.g. `fields[0]=from&fields[1]=to&populate[callRecord]=true`. Empty body when there are no calls.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "fields",
            "in": "query",
            "required": false,
            "description": "Fields to return, e.g. `fields[0]=from&fields[1]=to`.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "example": [
                "from",
                "to",
                "code"
              ]
            },
            "style": "deepObject",
            "explode": true
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Last call (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallsStatisticFlat"
                },
                "example": {
                  "id": 1001,
                  "from": "380661234567",
                  "to": "380441234567",
                  "code": "200",
                  "callRecord": {
                    "id": 77,
                    "name": "call-1001-mix.wav",
                    "ext": ".wav",
                    "mime": "audio/wave",
                    "size": 0.15,
                    "url": "/uploads/recordings/call-1001-mix.wav"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/statistics/get-my-last-call\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/get-my-last-call\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/get-my-last-call\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/recallWidgetStat/{id}": {
      "get": {
        "operationId": "listRecallWidgetCalls",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Calls of a recall widget",
        "description": "Call history filtered by the recall widget `id` (calls initiated through that website widget). Same query parameters and response as `GET /statistics`.\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Recall widget id.",
            "schema": {
              "type": "integer",
              "example": 4
            }
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Calls.",
                      "items": {
                        "$ref": "#/components/schemas/CallsStatisticItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 0,
                      "total": 0
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/statistics/recallWidgetStat/4\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/recallWidgetStat/4\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/recallWidgetStat/4\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/assignCommentary": {
      "put": {
        "operationId": "setCallCommentary",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Add a comment to a call",
        "description": "Sets the free-text `callCommentary` of a call of your company. Returns the updated call, or `data: null` if the call belongs to another company.\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "id",
                      "callCommentary"
                    ],
                    "description": "Target call and text.",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Call (statistic) id.",
                        "example": 1001
                      },
                      "callCommentary": {
                        "type": "string",
                        "description": "Comment text.",
                        "example": "Customer asked to call back tomorrow"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "id": 1001,
                  "callCommentary": "Customer asked to call back tomorrow"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated call.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallsStatisticItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1001,
                    "attributes": {
                      "from": "380661234567",
                      "to": "380441234567",
                      "code": "200",
                      "callCommentary": "Customer asked to call back tomorrow",
                      "createdAt": "2026-02-01T09:30:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/statistics/assignCommentary\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"id\": 1001,\n    \"callCommentary\": \"Customer asked to call back tomorrow\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/assignCommentary\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"id\": 1001,\n      \"callCommentary\": \"Customer asked to call back tomorrow\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/assignCommentary\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"id\":1001,\"callCommentary\":\"Customer asked to call back tomorrow\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/deleteCommentary": {
      "delete": {
        "operationId": "deleteCallCommentary",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Remove a call comment",
        "description": "Clears `callCommentary` (sets it to an empty string) of a call of your company. The call itself is kept.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None (only the comment text is cleared).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Call (statistic) id.",
            "schema": {
              "type": "integer",
              "example": 1001
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated call (`data: null` if not found in your company).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallsStatisticItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1001,
                    "attributes": {
                      "from": "380661234567",
                      "to": "380441234567",
                      "code": "200",
                      "callCommentary": "",
                      "createdAt": "2026-02-01T09:30:00.000Z",
                      "updatedAt": "2026-02-01T10:05:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/statistics/deleteCommentary?id=1001\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/deleteCommentary?id=1001\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/deleteCommentary?id=1001\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/deleteManyRecords": {
      "delete": {
        "operationId": "deleteManyCalls",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Delete several calls",
        "description": "Permanently deletes call-history records of your company by ids. Ids must be passed as an array:\n`?ids[0]=1001&ids[1]=1002`. Ids of other companies are ignored; if nothing matches — 400 `Nothing to delete!`.\nThe recording file (`callRecord`) is **not** removed from storage by this call, only the history record is deleted.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Irreversibly deletes the selected call-history records.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "description": "Array of call ids: `ids[0]=1001&ids[1]=1002`. A single scalar `ids=1001` is not accepted.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "example": [
                1001,
                1002
              ]
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Number of deleted records.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Deleted records.",
                      "example": 2
                    }
                  }
                },
                "example": {
                  "count": 2
                }
              }
            }
          },
          "400": {
            "description": "`Invalid params` (no `ids`) or `Nothing to delete!` (no matching calls in your company).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Nothing to delete!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/statistics/deleteManyRecords?ids=1001%2C1002\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/deleteManyRecords?ids=1001%2C1002\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/deleteManyRecords?ids=1001%2C1002\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/statistics/{id}": {
      "delete": {
        "operationId": "deleteCall",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Delete a call",
        "description": "Permanently deletes one call-history record of your company. Returns `{ \"id\": … }` of the deleted record, or `null` if not found in your company. The recording file is not removed from storage.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Irreversibly deletes the call-history record.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Call (statistic) id.",
            "schema": {
              "type": "integer",
              "example": 1001
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted record id.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "description": "Deleted call id.",
                      "example": 1001
                    }
                  }
                },
                "example": {
                  "id": 1001
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/statistics/1001\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/statistics/1001\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/statistics/1001\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/missed-calls": {
      "get": {
        "operationId": "listMissedCalls",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Missed calls",
        "description": "Returns missed-call entries of the company. Each entry links the employee (`user`) who missed the call and the call record (`statistic`).\nCompany owner and supervisor see all missed calls of the company; other roles see only their own.\nUse `populate[statistic]=true&populate[user][fields][0]=firstName` to get details; `populate[statistic][populate][contacts]=true`\nadditionally attaches matching address-book contacts. Filters apply to the missed-call entry, e.g.\n`filters[statistic][callSetupTime][$gte]=<unix seconds>`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of missed calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Missed calls.",
                      "items": {
                        "$ref": "#/components/schemas/CallsMissedCall"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 501,
                      "attributes": {
                        "createdAt": "2026-02-01T09:30:10.000Z",
                        "updatedAt": "2026-02-01T09:30:10.000Z",
                        "statistic": {
                          "data": {
                            "id": 1002,
                            "attributes": {
                              "from": "380661234567",
                              "to": "380441234567",
                              "code": "487",
                              "reason": "Request Terminated",
                              "callSetupTime": "1790931273"
                            }
                          }
                        },
                        "user": {
                          "data": {
                            "id": 7,
                            "attributes": {
                              "firstName": "John",
                              "lastName": "Doe"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/missed-calls\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/missed-calls\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/missed-calls\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/missed-calls/{id}": {
      "get": {
        "operationId": "getMissedCall",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Get missed call",
        "description": "Returns one missed-call entry by id (standard Strapi `findOne`, supports `populate`).\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Missed-call entry id.",
            "schema": {
              "type": "integer",
              "example": 501
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Missed call.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallsMissedCall"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 501,
                    "attributes": {
                      "createdAt": "2026-02-01T09:30:10.000Z",
                      "updatedAt": "2026-02-01T09:30:10.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/missed-calls/501\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/missed-calls/501\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/missed-calls/501\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/inCalls": {
      "get": {
        "operationId": "companyIncomingCallsBilling",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Incoming calls from billing",
        "description": "Returns the detailed list of **incoming** calls of the company for a period straight from the carrier billing (MediaCore),\nwith the billed cost per call. Dates are `YYYY-MM-DD HH:mm:ss` in UTC. The response is a table: `fields` are the column\nnames and each item of `data` is a row (array of values in the same order).\nColumns: `op_destination`, `tp_destination`, `op_caller_number`, `op_client_name`, `op_cost`, `tp_cost`, `duration`.\n**The period must not exceed 7 days** — otherwise 406 `Not acceptable: Time period is bigger than 7 days`.\nRequires the company to be linked to the billing; otherwise or when billing is unavailable the method answers 500.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "start_date",
            "in": "query",
            "required": true,
            "description": "Period start, `YYYY-MM-DD HH:mm:ss` (UTC).",
            "schema": {
              "type": "string",
              "example": "2026-02-01 00:00:00"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": true,
            "description": "Period end, `YYYY-MM-DD HH:mm:ss` (UTC). At most 7 days after `start_date`.",
            "schema": {
              "type": "string",
              "example": "2026-02-07 23:59:59"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max rows (default 100).",
            "schema": {
              "type": "integer",
              "default": 100,
              "example": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Billing rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallsBillingTable"
                },
                "example": {
                  "data": {
                    "attributes": {
                      "fields": [
                        "op_destination",
                        "tp_destination",
                        "op_caller_number",
                        "op_client_name",
                        "op_cost",
                        "tp_cost",
                        "duration"
                      ],
                      "data": [
                        [
                          "380441234567",
                          "380441234567",
                          "380661234567",
                          "Example LLC",
                          0,
                          0.02,
                          120
                        ]
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`start_date` or `end_date` must be defined!` / `start_date` and `end_date` must have next template `1990-01-01 00:00:00``.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "`start_date` and `end_date` must have next template `1990-01-01 00:00:00`",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "406": {
            "description": "Requested period is longer than 7 days.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 406,
                    "name": "ApplicationError",
                    "message": "Not acceptable: Time period is bigger than 7 days",
                    "details": {}
                  }
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/inCalls?start_date=2026-02-01%2000%3A00%3A00&end_date=2026-02-07%2023%3A59%3A59\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/inCalls?start_date=2026-02-01%2000%3A00%3A00&end_date=2026-02-07%2023%3A59%3A59\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/inCalls?start_date=2026-02-01%2000%3A00%3A00&end_date=2026-02-07%2023%3A59%3A59\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/outCalls": {
      "get": {
        "operationId": "companyOutgoingCallsBilling",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Outgoing calls from billing",
        "description": "Returns the detailed list of **outgoing** calls of the company for a period from the carrier billing (MediaCore) with the billed\ncost per call. Same format and rules as `GET /companies/inCalls`, including the **7-day maximum period** (406 otherwise).\nColumns: `op_caller_number`, `called_number_out`, `duration`, `op_cost`, `connect_time`, `disconnect_time`, `disconnect_cause`, `disconnect_cause_name`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "start_date",
            "in": "query",
            "required": true,
            "description": "Period start, `YYYY-MM-DD HH:mm:ss` (UTC).",
            "schema": {
              "type": "string",
              "example": "2026-02-01 00:00:00"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": true,
            "description": "Period end, `YYYY-MM-DD HH:mm:ss` (UTC). At most 7 days after `start_date`.",
            "schema": {
              "type": "string",
              "example": "2026-02-07 23:59:59"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max rows (default 100).",
            "schema": {
              "type": "integer",
              "default": 100,
              "example": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Billing rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallsBillingTable"
                },
                "example": {
                  "data": {
                    "attributes": {
                      "fields": [
                        "op_caller_number",
                        "called_number_out",
                        "duration",
                        "op_cost",
                        "connect_time",
                        "disconnect_time",
                        "disconnect_cause",
                        "disconnect_cause_name"
                      ],
                      "data": [
                        [
                          "380441234567",
                          "380661234567",
                          65,
                          0.05,
                          "2026-02-01 09:30:05",
                          "2026-02-01 09:31:10",
                          16,
                          "Normal call clearing"
                        ]
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`start_date`/`end_date` missing or not in `YYYY-MM-DD HH:mm:ss` format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "`start_date` or `end_date` must be defined!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "406": {
            "description": "Requested period is longer than 7 days.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 406,
                    "name": "ApplicationError",
                    "message": "Not acceptable: Time period is bigger than 7 days",
                    "details": {}
                  }
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/outCalls?start_date=2026-02-01%2000%3A00%3A00&end_date=2026-02-07%2023%3A59%3A59\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/outCalls?start_date=2026-02-01%2000%3A00%3A00&end_date=2026-02-07%2023%3A59%3A59\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/outCalls?start_date=2026-02-01%2000%3A00%3A00&end_date=2026-02-07%2023%3A59%3A59\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/voip/whisper-into-call": {
      "post": {
        "operationId": "whisperIntoCall",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Join a live call (whisper)",
        "description": "Step 1 of supervisor whispering: asks the media server to attach a WebRTC listening/whisper leg to a call that is in progress.\nThe response is an SDP **offer** plus the `to-tag` of the created dialog. The client must create a WebRTC answer and send it\nwith `POST /voip/answer-to-whispering`.\n\n`callId` is the id of the active call from the live calls feed (Socket.IO stream, key `active-calls:<callId>`), URL-encoded.\nThe body is forwarded to the media server as is; errors of the media server (unknown call, call already ended) surface as 500.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Attaches an additional media leg to a live call on the media server (the supervisor hears the call and can talk to the employee).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "callId"
                ],
                "properties": {
                  "callId": {
                    "type": "string",
                    "description": "Active call id (URL-encoded).",
                    "example": "1790931270-876649432%40203.0.113.10"
                  }
                }
              },
              "example": {
                "callId": "1790931270-876649432%40203.0.113.10"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SDP offer of the whisper leg.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sdp": {
                      "type": "string",
                      "description": "SDP offer to set as remote description.",
                      "example": "v=0\r\no=- 123 1 IN IP4 203.0.113.20\r\ns=-\r\nt=0 0\r\nm=audio 10000 UDP/TLS/RTP/SAVPF 111\r\n..."
                    },
                    "to-tag": {
                      "type": "string",
                      "description": "Dialog to-tag; pass it to `answer-to-whispering` as `toTag`.",
                      "example": "a1b2c3d4"
                    }
                  }
                },
                "example": {
                  "sdp": "v=0\r\no=- 123 1 IN IP4 203.0.113.20\r\ns=-\r\nt=0 0\r\nm=audio 10000 UDP/TLS/RTP/SAVPF 111\r\n...",
                  "to-tag": "a1b2c3d4"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/voip/whisper-into-call\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"callId\": \"1790931270-876649432%40203.0.113.10\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/voip/whisper-into-call\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"callId\": \"1790931270-876649432%40203.0.113.10\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/voip/whisper-into-call\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"callId\":\"1790931270-876649432%40203.0.113.10\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/voip/answer-to-whispering": {
      "post": {
        "operationId": "answerToWhispering",
        "tags": [
          "Calls & Statistics"
        ],
        "summary": "Complete joining a live call",
        "description": "Step 2 of supervisor whispering: sends the WebRTC SDP **answer** for the offer received from `POST /voip/whisper-into-call`.\nAfter this the media starts flowing to the supervisor's browser. Body is forwarded to the media server as is.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Starts the media stream of the whisper leg on the media server.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sdp",
                  "toTag",
                  "callId"
                ],
                "properties": {
                  "sdp": {
                    "type": "string",
                    "description": "Local SDP answer.",
                    "example": "v=0\r\no=- 456 1 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\n..."
                  },
                  "toTag": {
                    "type": "string",
                    "description": "`to-tag` from the whisper-into-call response.",
                    "example": "a1b2c3d4"
                  },
                  "callId": {
                    "type": "string",
                    "description": "Same `callId` as in whisper-into-call.",
                    "example": "1790931270-876649432%40203.0.113.10"
                  }
                }
              },
              "example": {
                "sdp": "v=0\r\no=- 456 1 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\n...",
                "toTag": "a1b2c3d4",
                "callId": "1790931270-876649432%40203.0.113.10"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Media server acknowledgement (JSON-RPC result, shape defined by the media server).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Result object of the media server.",
                  "additionalProperties": true
                },
                "example": {
                  "result": "ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/voip/answer-to-whispering\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"sdp\": \"v=0\\r\\no=- 456 1 IN IP4 0.0.0.0\\r\\ns=-\\r\\nt=0 0\\r\\nm=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n...\",\n  \"toTag\": \"a1b2c3d4\",\n  \"callId\": \"1790931270-876649432%40203.0.113.10\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/voip/answer-to-whispering\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"sdp\": \"v=0\\r\\no=- 456 1 IN IP4 0.0.0.0\\r\\ns=-\\r\\nt=0 0\\r\\nm=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n...\",\n    \"toTag\": \"a1b2c3d4\",\n    \"callId\": \"1790931270-876649432%40203.0.113.10\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/voip/answer-to-whispering\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"sdp\":\"v=0\\r\\no=- 456 1 IN IP4 0.0.0.0\\r\\ns=-\\r\\nt=0 0\\r\\nm=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n...\",\"toTag\":\"a1b2c3d4\",\"callId\":\"1790931270-876649432%40203.0.113.10\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/contacts": {
      "get": {
        "operationId": "listContacts",
        "tags": [
          "Contacts"
        ],
        "summary": "List contacts",
        "description": "Returns the contacts of the authenticated user's company (the company filter is added server-side). Standard Strapi query parameters are supported.\nUseful filters: `filters[number][$contains]=38044`, `filters[FIO][$containsi]=john`, `filters[allowlist][$eq]=true`. Sorting: `sort=FIO:asc`. Relations: `populate=statistics` (calls linked to the contact — see Calls & Statistics).\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Contacts.",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 31,
                      "attributes": {
                        "number": "380441234567",
                        "FIO": "John Doe",
                        "Comment": "Example LLC, purchasing",
                        "allowlist": false,
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z"
                      }
                    },
                    {
                      "id": 32,
                      "attributes": {
                        "number": "15551234567",
                        "FIO": "Spam caller",
                        "Comment": "",
                        "allowlist": true,
                        "createdAt": "2026-01-16T08:00:00.000Z",
                        "updatedAt": "2026-01-16T08:00:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 2
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/contacts\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/contacts\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/contacts\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Create a contact",
        "description": "Creates a contact in the company address book. Body: `{ \"data\": {...} }`. `company` is set from the token (a `company` sent in the body is overwritten). `number` must be unique within the company — store it in international format without `+` (e.g. `380441234567`), exactly as numbers appear in call history, so names are matched in the call log.\nIf `allowlist: true`, incoming calls from this number are blocked on all SIP domains of the company immediately (the number is written to the Kamailio block list).\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** With `allowlist: true` the number is added to the call block list of the company's SIP domains (incoming calls from it are rejected).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/ContactInput"
                      }
                    ],
                    "description": "Contact fields (Strapi envelope)."
                  }
                }
              },
              "example": {
                "data": {
                  "number": "380441234567",
                  "FIO": "John Doe",
                  "Comment": "Example LLC, purchasing",
                  "allowlist": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 31,
                    "attributes": {
                      "number": "380441234567",
                      "FIO": "John Doe",
                      "Comment": "Example LLC, purchasing",
                      "allowlist": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Number is already exists!` — a contact with this `number` already exists in the company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Number is already exists!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/contacts\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"number\": \"380441234567\",\n    \"FIO\": \"John Doe\",\n    \"Comment\": \"Example LLC, purchasing\",\n    \"allowlist\": false\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/contacts\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"number\": \"380441234567\",\n      \"FIO\": \"John Doe\",\n      \"Comment\": \"Example LLC, purchasing\",\n      \"allowlist\": false\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/contacts\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"number\":\"380441234567\",\"FIO\":\"John Doe\",\"Comment\":\"Example LLC, purchasing\",\"allowlist\":false}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "bulkDeleteContacts",
        "tags": [
          "Contacts"
        ],
        "summary": "Delete several contacts",
        "description": "Deletes contacts by id passed in the query string: one id `?id=31` or several `?id[0]=31&id[1]=32`. Returns the number of deleted rows (plain object, no Strapi envelope).\nBlocked numbers (`allowlist: true`) are removed from the block list before deletion.\nNote: the current implementation does not verify that the ids belong to your company — pass only ids received from `GET /contacts`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** Deletes contacts permanently; removes their numbers from the call block list.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Contact id(s): `id=31` or `id[0]=31&id[1]=32`.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "example": [
                31,
                32
              ]
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Number of deleted contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Deleted rows.",
                      "example": 2
                    }
                  }
                },
                "example": {
                  "count": 2
                }
              }
            }
          },
          "400": {
            "description": "`Id is required` — no `id` in the query string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Id is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/contacts?id=31%2C32\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/contacts?id=31%2C32\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/contacts?id=31%2C32\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/contacts/{id}": {
      "get": {
        "operationId": "getContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Get a contact",
        "description": "Returns one contact by id. `populate=statistics` adds the linked calls.\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Contact id.",
            "schema": {
              "type": "integer",
              "example": 31
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 31,
                    "attributes": {
                      "number": "380441234567",
                      "FIO": "John Doe",
                      "Comment": "Example LLC, purchasing",
                      "allowlist": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/contacts/31\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/contacts/31\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/contacts/31\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Update a contact",
        "description": "Updates a contact by id. Body: `{ \"data\": {...} }`; send only the fields to change. Changing `number` is allowed if the new number is not used by another contact of the company.\nThe block list is re-applied: the old number is removed from it, and the new state is written (`allowlist: true` → blocked on all company SIP domains, `false` → unblocked).\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** Updates the call block list of the company's SIP domains according to `allowlist`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Contact id.",
            "schema": {
              "type": "integer",
              "example": 31
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/ContactInput"
                      }
                    ],
                    "description": "Fields to update (Strapi envelope)."
                  }
                }
              },
              "example": {
                "data": {
                  "FIO": "John A. Doe",
                  "allowlist": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 31,
                    "attributes": {
                      "number": "380441234567",
                      "FIO": "John A. Doe",
                      "Comment": "Example LLC, purchasing",
                      "allowlist": true,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-17T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Number is already exists!` — another contact of the company already has this `number`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Number is already exists!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/contacts/31\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"FIO\": \"John A. Doe\",\n    \"allowlist\": true\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/contacts/31\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"FIO\": \"John A. Doe\",\n      \"allowlist\": true\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/contacts/31\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"FIO\":\"John A. Doe\",\"allowlist\":true}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Delete a contact",
        "description": "Deletes one contact by id. If the number was blocked (`allowlist: true`), it is removed from the block list first.\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** Deletes the contact permanently; removes the number from the call block list.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Contact id.",
            "schema": {
              "type": "integer",
              "example": 31
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 31,
                    "attributes": {
                      "number": "380441234567",
                      "FIO": "John Doe",
                      "Comment": "Example LLC, purchasing",
                      "allowlist": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/contacts/31\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/contacts/31\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/contacts/31\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "patch": {
        "operationId": "updateContactByPhone",
        "tags": [
          "Contacts"
        ],
        "summary": "Update a contact by phone number",
        "description": "Updates the name, note and/or block flag of the company's contact identified by its phone number — convenient when you only know the number (e.g. from a call record). Body: `{ \"data\": { \"number\": \"380441234567\", \"FIO\": \"...\", \"Comment\": \"...\", \"allowlist\": true } }`.\nImportant: the number is taken from `data.number` in the body, not from the `{id}` path segment (the path value is ignored, but must be present). Only `FIO`, `Comment` and `allowlist` can be changed. If no contact with this number exists in the company, nothing is updated and `data` is null.\nThe block list is re-applied according to `allowlist`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** Updates the call block list of the company's SIP domains according to `allowlist`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Phone number (informational — the server uses `data.number` from the body; send the same value here).",
            "schema": {
              "type": "string",
              "example": "380441234567"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Lookup number and fields to change.",
                    "required": [
                      "number"
                    ],
                    "properties": {
                      "number": {
                        "type": "string",
                        "description": "Phone number of the contact to update (as stored, without `+`).",
                        "example": "380441234567"
                      },
                      "FIO": {
                        "type": "string",
                        "description": "New display name.",
                        "example": "John Doe"
                      },
                      "Comment": {
                        "type": "string",
                        "description": "New note.",
                        "example": "VIP client"
                      },
                      "allowlist": {
                        "type": "boolean",
                        "description": "`true` — block incoming calls from this number; `false` — unblock.",
                        "example": true
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "number": "380441234567",
                  "FIO": "John Doe",
                  "Comment": "VIP client",
                  "allowlist": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated contact, or `data: null` if no contact with this number exists in the company.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 31,
                    "attributes": {
                      "number": "380441234567",
                      "FIO": "John Doe",
                      "Comment": "VIP client",
                      "allowlist": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-17T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PATCH \"https://api.onevoiplanet.com/api/contacts/380441234567\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"number\": \"380441234567\",\n    \"FIO\": \"John Doe\",\n    \"Comment\": \"VIP client\",\n    \"allowlist\": false\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/contacts/380441234567\", {\n  method: \"PATCH\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"number\": \"380441234567\",\n      \"FIO\": \"John Doe\",\n      \"Comment\": \"VIP client\",\n      \"allowlist\": false\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/contacts/380441234567\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PATCH\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"number\":\"380441234567\",\"FIO\":\"John Doe\",\"Comment\":\"VIP client\",\"allowlist\":false}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esims": {
      "get": {
        "operationId": "listEsimCountries",
        "tags": [
          "eSIM"
        ],
        "summary": "eSIM countries (catalogue)",
        "description": "Public catalogue of countries for which eSIM data bundles are sold. Returns **all** published countries at once\n(up to 1000, no pagination, `meta` is empty). The country matching the caller's IP address gets an extra flag `userCountry: true`.\n\nQuery options are accepted **only in object form**: `filters[iso_code][$eq]=AD`, `filters[name][$contains]=land`,\n`sort[0]=name:asc`, `fields[0]=name&fields[1]=iso_code`, `populate[image][fields][0]=url`, `populate[esimColour]=*`,\n`populate[esim_regions][fields][0]=name`, `populate[esim_bundles]=*`. Plain `populate=*` or `sort=name` are ignored.\nBundles of a country are better fetched via `GET /esim-bundles?filters[esim][id][$eq]={countryId}`.\n\n\n**Required role:** Public, Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort in array form only: `sort[0]=name:asc`.",
            "schema": {
              "type": "string",
              "example": "sort[0]=name:asc"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "required": false,
            "description": "Fields to return, array form: `fields[0]=name&fields[1]=iso_code`.",
            "schema": {
              "type": "string",
              "example": "fields[0]=name&fields[1]=iso_code"
            }
          },
          {
            "name": "populate",
            "in": "query",
            "required": false,
            "description": "Relations to include, object form only: `populate[image][fields][0]=url&populate[esimColour]=*&populate[esim_regions][fields][0]=name`.",
            "schema": {
              "type": "string",
              "example": "populate[image][fields][0]=url&populate[esimColour]=*"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "All published countries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Countries.",
                      "items": {
                        "$ref": "#/components/schemas/EsimCountry"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 4,
                      "attributes": {
                        "name": "Andorra",
                        "iso_code": "AD",
                        "createdAt": "2026-01-10T10:00:00.000Z",
                        "updatedAt": "2026-02-01T10:00:00.000Z",
                        "image": {
                          "data": {
                            "id": 501,
                            "attributes": {
                              "url": "/uploads/Andorra_1a2b3c.jpg"
                            }
                          }
                        },
                        "esimColour": {
                          "id": 61,
                          "firstColour": "0021EA",
                          "secondColour": "FEDF00",
                          "thirdColour": "000000"
                        },
                        "esim_regions": {
                          "data": [
                            {
                              "id": 4,
                              "attributes": {
                                "name": "Europe"
                              }
                            }
                          ]
                        }
                      }
                    },
                    {
                      "id": 9,
                      "attributes": {
                        "name": "Ukraine",
                        "iso_code": "UA",
                        "userCountry": true,
                        "createdAt": "2026-01-10T10:00:00.000Z",
                        "updatedAt": "2026-02-01T10:00:00.000Z",
                        "image": {
                          "data": {
                            "id": 502,
                            "attributes": {
                              "url": "/uploads/Ukraine_4d5e6f.jpg"
                            }
                          }
                        },
                        "esimColour": {
                          "id": 62,
                          "firstColour": "0057B7",
                          "secondColour": "FFD700",
                          "thirdColour": "000000"
                        },
                        "esim_regions": {
                          "data": [
                            {
                              "id": 4,
                              "attributes": {
                                "name": "Europe"
                              }
                            }
                          ]
                        }
                      }
                    }
                  ],
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esims\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esims\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esims\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esims/{id}": {
      "get": {
        "operationId": "getEsimCountry",
        "tags": [
          "eSIM"
        ],
        "summary": "eSIM country",
        "description": "Returns one catalogue country by id (Strapi core findOne). `populate=*` includes the flag image, colours, regions and bundles;\n`populate[esim_bundles][populate][prices][populate]=*` includes bundles with their prices.\n\n\n**Required role:** Public, Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Country id.",
            "schema": {
              "type": "integer",
              "example": 4
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Country.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EsimCountry"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 4,
                    "attributes": {
                      "name": "Andorra",
                      "iso_code": "AD",
                      "createdAt": "2026-01-10T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z",
                      "esimColour": {
                        "id": 61,
                        "firstColour": "0021EA",
                        "secondColour": "FEDF00",
                        "thirdColour": "000000"
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esims/4\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esims/4\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esims/4\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esim-regions": {
      "get": {
        "operationId": "listEsimRegions",
        "tags": [
          "eSIM"
        ],
        "summary": "eSIM regions (catalogue)",
        "description": "Public catalogue of regions (Europe, Asia, Global, …) for which multi-country eSIM bundles are sold. Strapi core list:\npagination, `sort=name:asc`, `filters[isGlobal][$eq]=true`, `populate[image][fields][0]=url`, `populate[esimColour]=*`,\n`populate[esim_countries][fields][0]=name` (countries covered by the region). Bundles of a region:\n`GET /esim-bundles?filters[esim_region][id][$eq]={regionId}`. Default page size is 10.\n\n\n**Required role:** Public, Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of regions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Regions.",
                      "items": {
                        "$ref": "#/components/schemas/EsimRegion"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 4,
                      "attributes": {
                        "name": "Europe",
                        "isGlobal": false,
                        "createdAt": "2026-01-10T10:00:00.000Z",
                        "updatedAt": "2026-02-01T10:00:00.000Z",
                        "image": {
                          "data": {
                            "id": 510,
                            "attributes": {
                              "url": "/uploads/Europe_7a8b9c.jpg"
                            }
                          }
                        },
                        "esimColour": {
                          "id": 71,
                          "firstColour": "1EB53A",
                          "secondColour": "FDD835",
                          "thirdColour": "000000"
                        },
                        "esim_countries": {
                          "data": [
                            {
                              "id": 4,
                              "attributes": {
                                "name": "Andorra"
                              }
                            },
                            {
                              "id": 9,
                              "attributes": {
                                "name": "Ukraine"
                              }
                            }
                          ]
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 1,
                      "total": 7
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esim-regions\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esim-regions\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esim-regions\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esim-regions/{id}": {
      "get": {
        "operationId": "getEsimRegion",
        "tags": [
          "eSIM"
        ],
        "summary": "eSIM region",
        "description": "Returns one region by id (Strapi core findOne). Use `populate` to include the image, colours, countries and bundles.\n\n**Required role:** Public, Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Region id.",
            "schema": {
              "type": "integer",
              "example": 4
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Region.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EsimRegion"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 4,
                    "attributes": {
                      "name": "Europe",
                      "isGlobal": false,
                      "createdAt": "2026-01-10T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z",
                      "esimColour": {
                        "id": 71,
                        "firstColour": "1EB53A",
                        "secondColour": "FDD835",
                        "thirdColour": "000000"
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esim-regions/4\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esim-regions/4\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esim-regions/4\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esim-bundles": {
      "get": {
        "operationId": "listEsimBundles",
        "tags": [
          "eSIM"
        ],
        "summary": "eSIM bundles (catalogue)",
        "description": "Public catalogue of eSIM data bundles. A bundle belongs either to a country (`esim`) or to a region (`esim_region`).\nThe retail price is in `prices` → `price_by_field` item named `price`, one price list per currency\n(`populate[prices][populate]=*` to get prices with their currency). The `id` of a bundle is what you pass as `esim.id`\nwhen creating an order (see Orders).\n\nUseful filters: `filters[esim][id][$eq]=4` (bundles of a country), `filters[esim_region][id][$eq]=4`,\n`filters[isFullEsim][$eq]=false` (data-only bundles; `true` — bundles with minutes/SMS), `filters[isInfinity][$eq]=true`\n(unlimited data), `filters[days_amount][$lte]=30`, `filters[providerName][$eq]=esimgo`. `sort=usage_amount:asc`.\nOnly published bundles are returned. Default page size is 10.\n\n\n**Required role:** Public, Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of bundles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Bundles.",
                      "items": {
                        "$ref": "#/components/schemas/EsimBundle"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 120,
                      "attributes": {
                        "days_amount": 7,
                        "usage_amount": 1,
                        "isInfinity": false,
                        "isFullEsim": false,
                        "minutes_amount": null,
                        "messages_amount": null,
                        "providerName": "esimgo",
                        "createdAt": "2026-01-10T10:00:00.000Z",
                        "updatedAt": "2026-02-01T10:00:00.000Z",
                        "esim": {
                          "data": {
                            "id": 4,
                            "attributes": {
                              "name": "Andorra",
                              "iso_code": "AD"
                            }
                          }
                        },
                        "esim_region": {
                          "data": null
                        },
                        "provider": [
                          {
                            "id": 300,
                            "__component": "esim-bundle-provider.esimgo",
                            "bundle_name": "esim_1GB_7D_AD_U"
                          }
                        ],
                        "prices": {
                          "data": [
                            {
                              "id": 900,
                              "attributes": {
                                "description": "1GB/7days Andorra",
                                "currency": {
                                  "data": {
                                    "id": 1,
                                    "attributes": {
                                      "name_short": "USD",
                                      "name_long": "US Dollar",
                                      "symbol": "$"
                                    }
                                  }
                                },
                                "price_by_field": [
                                  {
                                    "id": 9001,
                                    "name": "price",
                                    "price": 4.99,
                                    "isPriceStatic": false
                                  }
                                ]
                              }
                            },
                            {
                              "id": 901,
                              "attributes": {
                                "description": null,
                                "currency": {
                                  "data": {
                                    "id": 3,
                                    "attributes": {
                                      "name_short": "UAH",
                                      "name_long": "Гривня",
                                      "symbol": "₴"
                                    }
                                  }
                                },
                                "price_by_field": [
                                  {
                                    "id": 9002,
                                    "name": "price",
                                    "price": 219,
                                    "isPriceStatic": false
                                  }
                                ]
                              }
                            }
                          ]
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 135,
                      "total": 1346
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esim-bundles\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esim-bundles\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esim-bundles\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esim-bundles/{id}": {
      "get": {
        "operationId": "getEsimBundle",
        "tags": [
          "eSIM"
        ],
        "summary": "eSIM bundle",
        "description": "Returns one bundle by id (Strapi core findOne). `populate[prices][populate]=*&populate[esim]=*&populate[esim_region]=*&populate[provider]=*` gives prices with currency, the country/region and the provider descriptor.\n\n**Required role:** Public, Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Bundle id.",
            "schema": {
              "type": "integer",
              "example": 120
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Bundle.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EsimBundle"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 120,
                    "attributes": {
                      "days_amount": 7,
                      "usage_amount": 1,
                      "isInfinity": false,
                      "isFullEsim": false,
                      "minutes_amount": null,
                      "messages_amount": null,
                      "providerName": "esimgo",
                      "createdAt": "2026-01-10T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esim-bundles/120\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esim-bundles/120\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esim-bundles/120\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esim-datas": {
      "get": {
        "operationId": "listMyEsims",
        "tags": [
          "eSIM"
        ],
        "summary": "My eSIMs with live usage",
        "description": "Returns the eSIMs purchased by the **current user** (filter by user, not by company) together with live usage of every\nbundle (packet) on them, requested from the provider on each call (so the method can take several seconds).\nBy default eSIMs whose first bundle is a \"full\" eSIM (with minutes/SMS, `isFullEsim = true`) are excluded; pass any\n`filters[...]` (e.g. `filters[first_packet][isFullEsim][$eq]=true`, `filters[iccid][$eq]=8944...`) to replace the default filter.\nNo pagination: all matching eSIMs are returned, `meta` is empty.\n\nFor each eSIM: `iccid`, `lpa_code` (activation code for QR / manual install), `appleInstallUrl` (one-tap install link for iOS),\n`packets` — bundles newest first, each with `allowed_usage_mb`, `remaining_usage_mb`, `endTime`, `remaining_days`, `status`\n(provider status, e.g. `Active`, `Not Active`, `Expired`, `Finished`, `active`, `depleted`; derived from `endTime` when the\nprovider gives none; `null` — not activated yet), `mobileOperatorsInfo` — networks per country for the latest bundle,\n`first_packet` — the bundle the eSIM was bought with, including its country/region and image.\n\neSIMs are purchased through **Orders** (`POST /orders` with `esim: { \"id\": <bundleId> }`; to top up an existing eSIM add\n`\"iccid\"`). After the order is approved the eSIM appears here.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None (read-only requests to the eSIM providers).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Filters"
          }
        ],
        "responses": {
          "200": {
            "description": "eSIMs of the current user.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "eSIMs.",
                      "items": {
                        "$ref": "#/components/schemas/EsimMyEsim"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 15,
                      "attributes": {
                        "iccid": "8944538532000012345",
                        "lpa_code": "LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL",
                        "appleInstallUrl": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL",
                        "packets": [
                          {
                            "id": 31,
                            "templateId": "esim_1GB_7D_AD_U",
                            "esim_packet": {
                              "id": 120,
                              "days_amount": 7,
                              "usage_amount": 1,
                              "isInfinity": false,
                              "isFullEsim": false,
                              "providerName": "esimgo",
                              "minutes_amount": null,
                              "messages_amount": null,
                              "createdAt": "2026-01-10T10:00:00.000Z",
                              "updatedAt": "2026-02-01T10:00:00.000Z"
                            },
                            "allowed_usage_mb": 1000,
                            "remaining_usage_mb": 512.5,
                            "endTime": "2026-03-17T09:00:00.000Z",
                            "remaining_days": 5,
                            "status": "active"
                          }
                        ],
                        "mobileOperatorsInfo": [
                          {
                            "iso_code": "AD",
                            "mobileNetworks": [
                              "Andorra Telecom"
                            ]
                          }
                        ],
                        "first_packet": {
                          "data": {
                            "id": 120,
                            "attributes": {
                              "days_amount": 7,
                              "usage_amount": 1,
                              "isInfinity": false,
                              "isFullEsim": false,
                              "providerName": "esimgo",
                              "minutes_amount": null,
                              "messages_amount": null,
                              "createdAt": "2026-01-10T10:00:00.000Z",
                              "updatedAt": "2026-02-01T10:00:00.000Z",
                              "esim": {
                                "data": {
                                  "id": 4,
                                  "attributes": {
                                    "name": "Andorra",
                                    "iso_code": "AD",
                                    "createdAt": "2026-01-10T10:00:00.000Z",
                                    "updatedAt": "2026-02-01T10:00:00.000Z",
                                    "image": {
                                      "data": {
                                        "id": 501,
                                        "attributes": {
                                          "url": "/uploads/Andorra_1a2b3c.jpg",
                                          "name": "Andorra.jpg",
                                          "mime": "image/jpeg",
                                          "width": 612,
                                          "height": 388
                                        }
                                      }
                                    }
                                  }
                                }
                              },
                              "esim_region": {
                                "data": null
                              }
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esim-datas\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esim-datas\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esim-datas\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esim-datas/{id}": {
      "get": {
        "operationId": "getMyEsim",
        "tags": [
          "eSIM"
        ],
        "summary": "eSIM record",
        "description": "Returns the stored record of one eSIM by its id (Strapi core findOne) — **without** live usage from the provider\n(use `GET /esim-datas` for that). `populate[esim_packets][populate]=*` includes the attached bundles,\n`populate[first_packet]=*` — the first bundle, `populate[orders][fields][0]=orderId` — the orders it was bought/topped up with.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "eSIM record id (from `GET /esim-datas`).",
            "schema": {
              "type": "integer",
              "example": 15
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "eSIM record.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EsimRecord"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 15,
                    "attributes": {
                      "iccid": "8944538532000012345",
                      "provider": "esimgo",
                      "lpa_code": "LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL",
                      "appleInstallUrl": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp.example.com$ABCDEF-123456-GHIJKL",
                      "createdAt": "2026-03-10T09:00:00.000Z",
                      "updatedAt": "2026-03-10T09:00:00.000Z",
                      "esim_packets": [
                        {
                          "id": 31,
                          "templateId": "esim_1GB_7D_AD_U",
                          "esim_packet": {
                            "data": {
                              "id": 120,
                              "attributes": {
                                "days_amount": 7,
                                "usage_amount": 1,
                                "isInfinity": false,
                                "isFullEsim": false,
                                "providerName": "esimgo"
                              }
                            }
                          }
                        }
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esim-datas/15\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esim-datas/15\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esim-datas/15\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/esim-datas/getMobileOperators/{bundleId}": {
      "get": {
        "operationId": "getEsimBundleMobileOperators",
        "tags": [
          "eSIM"
        ],
        "summary": "Mobile networks of a bundle",
        "description": "Returns the mobile networks (operators) available with the given catalogue bundle, grouped by country, as reported by the\nbundle's provider (live request, may take a few seconds). The response is a **plain JSON array** without the `data` envelope.\n`mobileNetworks` is a list of operator names; for bundles of the `esimcard` provider it is a list of objects with the\nnetwork name and 2G/3G/4G/5G flags. An empty array means the provider no longer lists the bundle or has no network data.\n\n\n**Required role:** Public, Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None (read-only request to the eSIM provider).",
        "security": [],
        "parameters": [
          {
            "name": "bundleId",
            "in": "path",
            "required": true,
            "description": "Catalogue bundle id (from `GET /esim-bundles`).",
            "schema": {
              "type": "integer",
              "example": 120
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Networks grouped by country.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "description": "Countries with their networks.",
                  "items": {
                    "$ref": "#/components/schemas/EsimCountryNetworks"
                  }
                },
                "example": [
                  {
                    "iso_code": "AD",
                    "mobileNetworks": [
                      "Andorra Telecom"
                    ]
                  },
                  {
                    "iso_code": "ES",
                    "mobileNetworks": [
                      "Movistar",
                      "Orange",
                      "Vodafone"
                    ]
                  }
                ]
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "Unknown bundle id or provider error (the provider request failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 500,
                    "name": "InternalServerError",
                    "message": "Internal Server Error"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/esim-datas/getMobileOperators/120\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/esim-datas/getMobileOperators/120\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/esim-datas/getMobileOperators/120\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/create-integration/{serviceName}": {
      "post": {
        "operationId": "createIntegration",
        "tags": [
          "Integrations"
        ],
        "summary": "Connect a CRM integration",
        "description": "Connects a CRM to the company. One integration per CRM type. You provide the credentials of **your** CRM\naccount (API key, domain, webhook URL — see `IntegrationsFields`), and from then on the platform pushes call\nevents to the CRM automatically:\n* `Ringing` — incoming/outgoing call is ringing (SIP 100/180/183),\n* `inCall` — call answered (200 OK),\n* `NormalHangup` — call finished normally (200 after the dialog),\n* `Hangup` — call failed / busy / not answered (486 and other codes),\n* `Refered` — call transferred (302).\n\nEach event carries: `callId`, `from`, `to` (numbers without `+`), `callDirection` (true — incoming to the\ncompany, false — outgoing), `sip` / `sipTo` (employee SIP line as `<ext>domain<company-domain>`), `code`,\n`reason`, `callSetupTime`, `callStartTime`, `callEndTime` (Unix seconds), `ivr`, `group` (if the call went\nthrough an IVR/queue) and, when the recording is ready, `callRecord.url` (a follow-up event). Which events a\nCRM receives depends on the connector: most create a call/activity on hangup and attach the recording later;\nHubSpot/Pipedrive/Uspacy also create a contact for unknown numbers.\nBody is `{ \"data\": { ...fields } }`. Response: the company with the updated `integration` list.\nFor `zohocrm` use `POST /zohocrm/generate-tokens` instead; for `onebox` create the integration with\n`crmBaseUrl`, `login`, `restapipassword` and then call `POST /onebox/generate-token`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Stores the CRM credentials and enables automatic push of call events (and later call recordings) to the CRM; may create contacts/activities in the CRM.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "serviceName",
            "in": "path",
            "required": true,
            "description": "CRM type.",
            "schema": {
              "type": "string",
              "enum": [
                "keycrm",
                "salesdrive",
                "nethunt",
                "zohocrm",
                "pipedrive",
                "onebox",
                "hubspot",
                "uspacy"
              ],
              "example": "keycrm"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/IntegrationsFields"
                  }
                }
              },
              "examples": {
                "keycrm": {
                  "summary": "KeyCRM",
                  "value": {
                    "data": {
                      "webhookUrl": "https://openapi.keycrm.app/v1/communication/call-route/********",
                      "active": true
                    }
                  }
                },
                "salesdrive": {
                  "summary": "SalesDrive",
                  "value": {
                    "data": {
                      "crmBaseUrl": "https://example.salesdrive.me",
                      "crmApiKey": "********",
                      "active": true
                    }
                  }
                },
                "nethunt": {
                  "summary": "NetHunt",
                  "value": {
                    "data": {
                      "email": "owner@example.com",
                      "apiKey": "********",
                      "active": true
                    }
                  }
                },
                "pipedrive": {
                  "summary": "Pipedrive",
                  "value": {
                    "data": {
                      "api_token": "********",
                      "active": true
                    }
                  }
                },
                "hubspot": {
                  "summary": "HubSpot",
                  "value": {
                    "data": {
                      "api_token": "pat-eu1-********",
                      "active": true
                    }
                  }
                },
                "uspacy": {
                  "summary": "Uspacy",
                  "value": {
                    "data": {
                      "domain": "example.uspacy.ua",
                      "email": "owner@example.com",
                      "jwt": "********",
                      "refresh_token": "********",
                      "active": true
                    }
                  }
                },
                "onebox": {
                  "summary": "OneBox",
                  "value": {
                    "data": {
                      "crmBaseUrl": "https://example.1b.app",
                      "login": "owner@example.com",
                      "restapipassword": "********",
                      "active": true
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Company with integrations (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationsCompanyResult"
                },
                "example": {
                  "id": 7,
                  "Name": "Example LLC",
                  "integration": [
                    {
                      "id": 2,
                      "__component": "integration.keycrm",
                      "serviceName": "keycrm",
                      "keycrm": {
                        "id": 5,
                        "webhookUrl": "https://openapi.keycrm.app/v1/communication/call-route/********",
                        "active": true,
                        "createdAt": "2026-10-02T10:00:00.000Z",
                        "updatedAt": "2026-10-02T10:00:00.000Z"
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Integration of this type already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Already Exists!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/companies/create-integration/keycrm\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"webhookUrl\": \"https://openapi.keycrm.app/v1/communication/call-route/********\",\n    \"active\": true\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/create-integration/keycrm\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"webhookUrl\": \"https://openapi.keycrm.app/v1/communication/call-route/********\",\n      \"active\": true\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/create-integration/keycrm\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"webhookUrl\":\"https://openapi.keycrm.app/v1/communication/call-route/********\",\"active\":true}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/edit-integration/{serviceName}/{id}": {
      "post": {
        "operationId": "editIntegration",
        "tags": [
          "Integrations"
        ],
        "summary": "Edit a CRM integration",
        "description": "Updates the credentials or the `active` flag of an existing integration. `id` is the integration record id\n(`integration[].<serviceName>.id` in `GET /companies/my`), not the component id. `active: false` pauses the\npush of call events without deleting the credentials. Body is `{ \"data\": { ...fields } }`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Changes stored CRM credentials; pausing/resuming call event push.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "serviceName",
            "in": "path",
            "required": true,
            "description": "CRM type.",
            "schema": {
              "type": "string",
              "enum": [
                "keycrm",
                "salesdrive",
                "nethunt",
                "zohocrm",
                "pipedrive",
                "onebox",
                "hubspot",
                "uspacy"
              ],
              "example": "keycrm"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Integration record id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/IntegrationsFields"
                  }
                }
              },
              "example": {
                "data": {
                  "active": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated integration record (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationsRecord"
                },
                "example": {
                  "id": 5,
                  "webhookUrl": "https://openapi.keycrm.app/v1/communication/call-route/********",
                  "active": false,
                  "createdAt": "2026-10-02T10:00:00.000Z",
                  "updatedAt": "2026-10-02T10:05:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The company has no integration of this type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Integration Not Found!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/companies/edit-integration/keycrm/5\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"active\": false\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/edit-integration/keycrm/5\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"active\": false\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/edit-integration/keycrm/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"active\":false}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/delete-integration/{serviceName}/{id}": {
      "delete": {
        "operationId": "deleteIntegration",
        "tags": [
          "Integrations"
        ],
        "summary": "Disconnect a CRM integration",
        "description": "Deletes the integration record (credentials) and removes it from the company. Call events stop being pushed. `id` is the integration record id as in edit.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Permanently deletes the stored CRM credentials; stops call event push.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "serviceName",
            "in": "path",
            "required": true,
            "description": "CRM type.",
            "schema": {
              "type": "string",
              "enum": [
                "keycrm",
                "salesdrive",
                "nethunt",
                "zohocrm",
                "pipedrive",
                "onebox",
                "hubspot",
                "uspacy"
              ],
              "example": "keycrm"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Integration record id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Company with the remaining integrations (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationsCompanyResult"
                },
                "example": {
                  "id": 7,
                  "Name": "Example LLC",
                  "integration": []
                }
              }
            }
          },
          "400": {
            "description": "The company has no integration of this type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Integration Not Found!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/companies/delete-integration/keycrm/5\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/delete-integration/keycrm/5\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/delete-integration/keycrm/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/onebox/generate-token": {
      "post": {
        "operationId": "oneboxGenerateToken",
        "tags": [
          "Integrations"
        ],
        "summary": "OneBox: obtain API token",
        "description": "Exchanges the OneBox login and REST API password for an API token at `<crmBaseUrl>/api/v2/token/get/` and\nstores the token in the company's existing `onebox` integration. Create the integration first\n(`POST /companies/create-integration/onebox`). Body is flat (no `data` wrapper).\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Calls your OneBox instance and stores the received token in the integration.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "login",
                  "restapipassword",
                  "crmBaseUrl"
                ],
                "properties": {
                  "login": {
                    "type": "string",
                    "description": "OneBox user login.",
                    "example": "owner@example.com"
                  },
                  "restapipassword": {
                    "type": "string",
                    "description": "OneBox REST API password.",
                    "example": "********"
                  },
                  "crmBaseUrl": {
                    "type": "string",
                    "description": "Base URL of your OneBox instance.",
                    "example": "https://example.1b.app"
                  }
                }
              },
              "example": {
                "login": "owner@example.com",
                "restapipassword": "********",
                "crmBaseUrl": "https://example.1b.app"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token obtained and stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "boolean",
                  "description": "Always true on success.",
                  "example": true
                },
                "example": true
              }
            }
          },
          "400": {
            "description": "OneBox rejected the credentials or the integration does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "error Onebox create token",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/onebox/generate-token\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"login\": \"owner@example.com\",\n  \"restapipassword\": \"********\",\n  \"crmBaseUrl\": \"https://example.1b.app\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/onebox/generate-token\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"login\": \"owner@example.com\",\n    \"restapipassword\": \"********\",\n    \"crmBaseUrl\": \"https://example.1b.app\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/onebox/generate-token\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"login\":\"owner@example.com\",\"restapipassword\":\"********\",\"crmBaseUrl\":\"https://example.1b.app\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/zohocrm/generate-tokens": {
      "post": {
        "operationId": "zohocrmGenerateTokens",
        "tags": [
          "Integrations"
        ],
        "summary": "Zoho CRM: connect via OAuth code",
        "description": "Completes the Zoho OAuth flow: exchanges the authorization `code` (received on the platform's redirect URI\nafter the user grants access) for access/refresh tokens and creates the `zohocrm` integration of the company.\nTokens are refreshed automatically afterwards. The response body is empty; errors from Zoho are not returned\nto the client (only logged) — verify the result via `GET /companies/my` (`integration[].serviceName = \"zohocrm\"`).\nBody is flat (no `data` wrapper).\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Calls Zoho OAuth, stores access/refresh tokens and creates the zohocrm integration (enables call event push).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "Zoho OAuth authorization code.",
                    "example": "1000.abc123********"
                  }
                }
              },
              "example": {
                "code": "1000.abc123********"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request processed; body is empty. Check `GET /companies/my` for the new integration."
          },
          "400": {
            "description": "Integration already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Already Exists!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/zohocrm/generate-tokens\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"code\": \"1000.abc123********\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/zohocrm/generate-tokens\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"code\": \"1000.abc123********\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/zohocrm/generate-tokens\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"code\":\"1000.abc123********\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/crm-token/create-token": {
      "post": {
        "operationId": "createCrmApiToken",
        "tags": [
          "Integrations"
        ],
        "summary": "Create an API token for an external system",
        "description": "Creates a read-only API token for external systems (CRM, BI). The token is valid for **7 days** and grants\nonly: `GET /companies/my`, `GET /statistics/myStat` (call statistics) and `GET /companies/getEmployees`.\nPass it as `Authorization: Bearer <accessKey>`. The `accessKey` is returned only once — store it.\n**Note:** this permission is currently not granted to any client role; it must be enabled by the platform\nadministrator for your role before use (otherwise 403).\nBody is flat (no `data` wrapper).\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Creates an API token (7-day lifespan) bound to the company.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tokenName"
                ],
                "properties": {
                  "tokenName": {
                    "type": "string",
                    "description": "Token label; stored as `<tokenName> crm`.",
                    "example": "Bitrix sync"
                  }
                }
              },
              "example": {
                "tokenName": "Bitrix sync"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created token.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Token record.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Record id.",
                          "example": 3
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Attributes.",
                          "properties": {
                            "createdAt": {
                              "type": "string",
                              "format": "date-time",
                              "description": "Created at.",
                              "example": "2026-10-02T10:00:00.000Z"
                            },
                            "updatedAt": {
                              "type": "string",
                              "format": "date-time",
                              "description": "Updated at.",
                              "example": "2026-10-02T10:00:00.000Z"
                            },
                            "apiToken": {
                              "type": "object",
                              "description": "API token details.",
                              "properties": {
                                "id": {
                                  "type": "integer",
                                  "description": "Token id.",
                                  "example": 11
                                },
                                "name": {
                                  "type": "string",
                                  "description": "Token name.",
                                  "example": "Bitrix sync crm"
                                },
                                "description": {
                                  "type": "string",
                                  "description": "Description.",
                                  "example": "api token to get access to crm"
                                },
                                "type": {
                                  "type": "string",
                                  "description": "Always `custom`.",
                                  "example": "custom"
                                },
                                "accessKey": {
                                  "type": "string",
                                  "description": "Bearer token value (shown once).",
                                  "example": "********"
                                },
                                "lifespan": {
                                  "type": "integer",
                                  "description": "Lifespan in ms (604800000 = 7 days).",
                                  "example": 604800000
                                },
                                "expiresAt": {
                                  "type": "string",
                                  "format": "date-time",
                                  "description": "Expiry.",
                                  "example": "2026-10-09T10:00:00.000Z"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 3,
                    "attributes": {
                      "createdAt": "2026-10-02T10:00:00.000Z",
                      "updatedAt": "2026-10-02T10:00:00.000Z",
                      "apiToken": {
                        "id": 11,
                        "name": "Bitrix sync crm",
                        "description": "api token to get access to crm",
                        "type": "custom",
                        "accessKey": "********",
                        "lifespan": 604800000,
                        "expiresAt": "2026-10-09T10:00:00.000Z"
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/crm-token/create-token\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"tokenName\": \"Bitrix sync\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/crm-token/create-token\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"tokenName\": \"Bitrix sync\"\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/crm-token/create-token\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"tokenName\":\"Bitrix sync\"}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/ivrs": {
      "get": {
        "operationId": "listIvrs",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "List IVR menus",
        "description": "Returns the IVR menus of the authenticated user's company (the company filter is added server-side, you cannot see other companies' IVRs).\nStandard Strapi query parameters are supported. Useful `populate` values: `domain`, `ivr_work_times`, `redirection_data`, `in_phone_lines`, `hold_time_expired_audio`, `max_input_exided_audio`, `wrong_input_audio`.\nUseful filters: `filters[domain][id][$eq]=12`, `filters[name][$contains]=Sales`.\nTo build the full menu tree, request `/ivr-work-times?filters[work_time][id][$eq]=<ivrId>&populate=*` and `/ivr-menu-items?filters[ivr_work_time][id][$eq]=<workTimeId>&populate=*`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of IVR menus.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "IVR menus.",
                      "items": {
                        "$ref": "#/components/schemas/Ivr"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 7,
                      "attributes": {
                        "name": "Main menu",
                        "description": "Greeting and department selection",
                        "number": "000",
                        "hold_time": 5,
                        "max_input": 3,
                        "redirection": "group",
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-16T09:30:00.000Z",
                        "domain": {
                          "data": {
                            "id": 12,
                            "attributes": {
                              "name": "examplellc"
                            }
                          }
                        },
                        "ivr_work_times": {
                          "data": [
                            {
                              "id": 21,
                              "attributes": {
                                "type": "byDates",
                                "date": {
                                  "mon": "09:00-18:00",
                                  "tue": "09:00-18:00",
                                  "wed": "09:00-18:00",
                                  "thu": "09:00-18:00",
                                  "fri": "09:00-18:00"
                                },
                                "use_global_redirect": true
                              }
                            },
                            {
                              "id": 22,
                              "attributes": {
                                "type": "any",
                                "date": null,
                                "use_global_redirect": true
                              }
                            }
                          ]
                        },
                        "redirection_data": {
                          "id": 31,
                          "call_end": false,
                          "extension": null
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/ivrs\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivrs\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivrs\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createIvr",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Create an IVR menu",
        "description": "Creates an IVR menu in one of the company's SIP domains. The body is the Strapi envelope `{ \"data\": {...} }`.\nThe internal 3-digit IVR number (`number`) is generated automatically (first free `000`–`999` in the domain); a `number` sent in the body is ignored. `company` is set from the token.\nAudio fields reference the company's audio files (`/company-audios`, type `sound`).\nCreating the IVR alone does not change call routing yet: add working-time blocks (`POST /ivr-work-times`) and menu items (`POST /ivr-menu-items`), then attach a phone number to the IVR through the Numbers API (`addInPhone`). The Asterisk dialplan of the IVR is (re)generated on every `PUT /ivrs/{id}`, `PUT /ivr-work-times/{id}` and `POST /ivr-menu-items`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Creates a record only; the telephony dialplan is not touched until the IVR is updated or its menu items are saved.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/IvrInput"
                      }
                    ],
                    "description": "IVR fields (Strapi envelope). `name` and `domain` are required."
                  }
                }
              },
              "example": {
                "data": {
                  "name": "Main menu",
                  "description": "Greeting and department selection",
                  "domain": 12,
                  "hold_time": 5,
                  "max_input": 3,
                  "redirection": "group",
                  "redirection_data": {
                    "group": 5,
                    "call_end": false
                  },
                  "hold_time_expired_audio": 101,
                  "wrong_input_audio": 102,
                  "max_input_exided_audio": 102
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created IVR.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Ivr"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 7,
                    "attributes": {
                      "name": "Main menu",
                      "description": "Greeting and department selection",
                      "number": "001",
                      "hold_time": 5,
                      "max_input": 3,
                      "redirection": "group",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Messages: `Domain required!`, `Domain not found` (domain does not belong to the company), `name must be at least 3 characters`, `Всю номери зайняті в даному пространстві!` (all 1000 IVR numbers in the domain are taken).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Domain required!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/ivrs\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"name\": \"Main menu\",\n    \"description\": \"Greeting and department selection\",\n    \"domain\": 12,\n    \"hold_time\": 5,\n    \"max_input\": 3,\n    \"redirection\": \"group\",\n    \"redirection_data\": {\n      \"group\": 5,\n      \"call_end\": false\n    },\n    \"hold_time_expired_audio\": 101,\n    \"wrong_input_audio\": 102,\n    \"max_input_exided_audio\": 102\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivrs\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"name\": \"Main menu\",\n      \"description\": \"Greeting and department selection\",\n      \"domain\": 12,\n      \"hold_time\": 5,\n      \"max_input\": 3,\n      \"redirection\": \"group\",\n      \"redirection_data\": {\n        \"group\": 5,\n        \"call_end\": false\n      },\n      \"hold_time_expired_audio\": 101,\n      \"wrong_input_audio\": 102,\n      \"max_input_exided_audio\": 102\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivrs\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"name\":\"Main menu\",\"description\":\"Greeting and department selection\",\"domain\":12,\"hold_time\":5,\"max_input\":3,\"redirection\":\"group\",\"redirection_data\":{\"group\":5,\"call_end\":false},\"hold_time_expired_audio\":101,\"wrong_input_audio\":102,\"max_input_exided_audio\":102}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/ivrs/{id}": {
      "get": {
        "operationId": "getIvr",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Get an IVR menu",
        "description": "Returns one IVR of the company. Returns `data: null` if the IVR does not exist or belongs to another company. Use `populate` to include relations (e.g. `populate[0]=ivr_work_times&populate[1]=redirection_data&populate[2]=domain`).\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "IVR id.",
            "schema": {
              "type": "integer",
              "example": 7
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "IVR, or `data: null` when not found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Ivr"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 7,
                    "attributes": {
                      "name": "Main menu",
                      "description": "Greeting and department selection",
                      "number": "001",
                      "hold_time": 5,
                      "max_input": 3,
                      "redirection": "group",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-16T09:30:00.000Z",
                      "domain": {
                        "data": {
                          "id": 12,
                          "attributes": {
                            "name": "examplellc"
                          }
                        }
                      },
                      "redirection_data": {
                        "id": 31,
                        "call_end": false,
                        "extension": null,
                        "group": {
                          "data": {
                            "id": 5,
                            "attributes": {
                              "name": "Sales",
                              "number": "001"
                            }
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/ivrs/7\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivrs/7\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivrs/7\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateIvr",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Update an IVR menu",
        "description": "Updates IVR settings (name, timeouts, audio, final redirect). Body: `{ \"data\": {...} }`. `domain` and `number` cannot be changed — they are removed from the body.\nAfter the update the Asterisk dialplan of this IVR is regenerated synchronously from the current IVR, its working-time blocks and menu items, so the change applies to incoming calls immediately after the response.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Regenerates the IVR dialplan in Asterisk (incoming call routing changes immediately).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "IVR id.",
            "schema": {
              "type": "integer",
              "example": 7
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/IvrInput"
                      }
                    ],
                    "description": "Fields to update (Strapi envelope). `domain` and `number` are ignored."
                  }
                }
              },
              "example": {
                "data": {
                  "name": "Main menu (updated)",
                  "hold_time": 7,
                  "redirection": "employee",
                  "redirection_data": {
                    "user": 44,
                    "call_end": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated IVR.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Ivr"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 7,
                    "attributes": {
                      "name": "Main menu (updated)",
                      "description": "Greeting and department selection",
                      "number": "001",
                      "hold_time": 7,
                      "max_input": 3,
                      "redirection": "employee",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-17T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/ivrs/7\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"name\": \"Main menu (updated)\",\n    \"hold_time\": 7,\n    \"redirection\": \"employee\",\n    \"redirection_data\": {\n      \"user\": 44,\n      \"call_end\": false\n    }\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivrs/7\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"name\": \"Main menu (updated)\",\n      \"hold_time\": 7,\n      \"redirection\": \"employee\",\n      \"redirection_data\": {\n        \"user\": 44,\n        \"call_end\": false\n      }\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivrs/7\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"name\":\"Main menu (updated)\",\"hold_time\":7,\"redirection\":\"employee\",\"redirection_data\":{\"user\":44,\"call_end\":false}}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteIvr",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Delete an IVR menu",
        "description": "Deletes an IVR. Refused while a phone number is routed to the IVR (detach it first through the Numbers API) or while a recall widget redirects to it.\nWorking-time blocks and menu items that belong to the IVR are not deleted automatically (they remain as orphans).\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes the IVR record permanently.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "IVR id.",
            "schema": {
              "type": "integer",
              "example": 7
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted IVR (or `data: null` if nothing was deleted).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Ivr"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 7,
                    "attributes": {
                      "name": "Main menu",
                      "description": null,
                      "number": "001",
                      "hold_time": 5,
                      "max_input": 3,
                      "redirection": "group",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-16T09:30:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "IVR is still in use. `IVR is attached to phone!` — `details.inPhone` lists the attached numbers; `Remove recall_widget before remove group` — `details.recallWidgetName` lists the widgets.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "IVR is attached to phone!",
                    "details": {
                      "inPhone": [
                        {
                          "number": "+380441234567",
                          "phone_prefix": null,
                          "country_prefix": null
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/ivrs/7\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivrs/7\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivrs/7\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/ivr-work-times": {
      "get": {
        "operationId": "listIvrWorkTimes",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "List IVR working-time blocks",
        "description": "Returns working-time blocks of IVR menus. A block of type `byDates` is active in the given weekday/time ranges (Europe/Kyiv time); a block of type `any` is the fallback used outside those ranges (e.g. the \"we are closed\" announcement). Each block has its own greeting audio and menu items.\nAlways filter by your IVR: `filters[work_time][id][$eq]=7`. Useful `populate`: `hello_audios`, `ivr_menu_description`, `background_audio`, `ivr_menu_items`, `work_time`.\nThe `date` object is returned with weekdays ordered mon → sun.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of working-time blocks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Working-time blocks.",
                      "items": {
                        "$ref": "#/components/schemas/IvrWorkTime"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 21,
                      "attributes": {
                        "type": "byDates",
                        "date": {
                          "mon": "09:00-18:00",
                          "tue": "09:00-18:00",
                          "wed": "09:00-18:00",
                          "thu": "09:00-18:00",
                          "fri": "09:00-18:00"
                        },
                        "use_global_redirect": true,
                        "createdAt": "2026-01-15T10:05:00.000Z",
                        "updatedAt": "2026-01-15T10:05:00.000Z",
                        "hello_audios": {
                          "data": [
                            {
                              "id": 101,
                              "attributes": {
                                "name": "Greeting",
                                "type": "sound",
                                "category": "additional",
                                "url": "/uploads/sound/company_1/greeting.wav",
                                "size": 120.5
                              }
                            }
                          ]
                        },
                        "ivr_menu_description": {
                          "data": {
                            "id": 103,
                            "attributes": {
                              "name": "Menu description",
                              "type": "sound",
                              "category": "additional",
                              "url": "/uploads/sound/company_1/menu.wav",
                              "size": 80.2
                            }
                          }
                        },
                        "background_audio": {
                          "data": null
                        },
                        "work_time": {
                          "data": {
                            "id": 7,
                            "attributes": {
                              "name": "Main menu",
                              "number": "001"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/ivr-work-times\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-work-times\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-work-times\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createIvrWorkTime",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Create an IVR working-time block",
        "description": "Adds a working-time block to an IVR (`work_time` = IVR id). Body: `{ \"data\": {...} }`.\n`type: byDates` requires `date` — an object with weekday keys `mon`…`sun` and values `HH:MM-HH:MM` (Europe/Kyiv). `type: any` is the fallback block (usually one per IVR, no `date`).\nCreating a block does not regenerate the dialplan by itself; it is picked up on the next `PUT /ivrs/{id}`, `PUT /ivr-work-times/{id}` or `POST /ivr-menu-items`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Creates a record only.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/IvrWorkTimeInput"
                      }
                    ],
                    "description": "Working-time block fields (Strapi envelope). `type` is required."
                  }
                }
              },
              "example": {
                "data": {
                  "type": "byDates",
                  "date": {
                    "mon": "09:00-18:00",
                    "tue": "09:00-18:00",
                    "wed": "09:00-18:00",
                    "thu": "09:00-18:00",
                    "fri": "09:00-18:00"
                  },
                  "work_time": 7,
                  "hello_audios": [
                    101
                  ],
                  "ivr_menu_description": 103,
                  "use_global_redirect": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created block.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/IvrWorkTime"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 21,
                    "attributes": {
                      "type": "byDates",
                      "date": {
                        "mon": "09:00-18:00",
                        "tue": "09:00-18:00",
                        "wed": "09:00-18:00",
                        "thu": "09:00-18:00",
                        "fri": "09:00-18:00"
                      },
                      "use_global_redirect": true,
                      "createdAt": "2026-01-15T10:05:00.000Z",
                      "updatedAt": "2026-01-15T10:05:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error, e.g. `type must be one of the following values: any, byDates`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "type is a required field",
                    "details": {
                      "errors": [
                        {
                          "path": [
                            "type"
                          ],
                          "message": "type is a required field",
                          "name": "ValidationError"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/ivr-work-times\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"type\": \"byDates\",\n    \"date\": {\n      \"mon\": \"09:00-18:00\",\n      \"tue\": \"09:00-18:00\",\n      \"wed\": \"09:00-18:00\",\n      \"thu\": \"09:00-18:00\",\n      \"fri\": \"09:00-18:00\"\n    },\n    \"work_time\": 7,\n    \"hello_audios\": [\n      101\n    ],\n    \"ivr_menu_description\": 103,\n    \"use_global_redirect\": true\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-work-times\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"type\": \"byDates\",\n      \"date\": {\n        \"mon\": \"09:00-18:00\",\n        \"tue\": \"09:00-18:00\",\n        \"wed\": \"09:00-18:00\",\n        \"thu\": \"09:00-18:00\",\n        \"fri\": \"09:00-18:00\"\n      },\n      \"work_time\": 7,\n      \"hello_audios\": [\n        101\n      ],\n      \"ivr_menu_description\": 103,\n      \"use_global_redirect\": true\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-work-times\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"type\":\"byDates\",\"date\":{\"mon\":\"09:00-18:00\",\"tue\":\"09:00-18:00\",\"wed\":\"09:00-18:00\",\"thu\":\"09:00-18:00\",\"fri\":\"09:00-18:00\"},\"work_time\":7,\"hello_audios\":[101],\"ivr_menu_description\":103,\"use_global_redirect\":true}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/ivr-work-times/{id}": {
      "get": {
        "operationId": "getIvrWorkTime",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Get an IVR working-time block",
        "description": "Returns one working-time block by id. Use `populate` to include audio files, menu items and the parent IVR (`work_time`).\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Working-time block id.",
            "schema": {
              "type": "integer",
              "example": 21
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Working-time block.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/IvrWorkTime"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 21,
                    "attributes": {
                      "type": "byDates",
                      "date": {
                        "mon": "09:00-18:00",
                        "fri": "09:00-18:00"
                      },
                      "use_global_redirect": true,
                      "createdAt": "2026-01-15T10:05:00.000Z",
                      "updatedAt": "2026-01-15T10:05:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/ivr-work-times/21\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-work-times/21\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-work-times/21\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateIvrWorkTime",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Update an IVR working-time block",
        "description": "Updates a working-time block (schedule, greeting audio, fallback flag). Body: `{ \"data\": {...} }`.\nImportant: if after the update the block has no `ivr_menu_description` audio (`null`), ALL menu items of this block are deleted — a block without a menu announcement is treated as \"announcement only, no menu\".\nAfter the update the dialplan of the parent IVR is regenerated synchronously, so incoming calls follow the new schedule immediately. The block must be attached to an IVR (`work_time`), otherwise the request fails with 500.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Regenerates the IVR dialplan in Asterisk; deletes all menu items of the block if `ivr_menu_description` becomes null.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Working-time block id.",
            "schema": {
              "type": "integer",
              "example": 21
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/IvrWorkTimeInput"
                      }
                    ],
                    "description": "Fields to update (Strapi envelope)."
                  }
                }
              },
              "example": {
                "data": {
                  "date": {
                    "mon": "08:00-20:00",
                    "tue": "08:00-20:00",
                    "wed": "08:00-20:00",
                    "thu": "08:00-20:00",
                    "fri": "08:00-20:00",
                    "sat": "10:00-16:00"
                  },
                  "hello_audios": [
                    101,
                    104
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated block. `ivr_menu_items` is always returned as an empty array here; `ivr_menu_description` is populated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/IvrWorkTime"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 21,
                    "attributes": {
                      "type": "byDates",
                      "date": {
                        "mon": "08:00-20:00",
                        "tue": "08:00-20:00",
                        "wed": "08:00-20:00",
                        "thu": "08:00-20:00",
                        "fri": "08:00-20:00",
                        "sat": "10:00-16:00"
                      },
                      "use_global_redirect": true,
                      "createdAt": "2026-01-15T10:05:00.000Z",
                      "updatedAt": "2026-01-17T12:00:00.000Z",
                      "ivr_menu_description": {
                        "data": {
                          "id": 103,
                          "attributes": {
                            "name": "Menu description",
                            "type": "sound",
                            "category": "additional",
                            "url": "/uploads/sound/company_1/menu.wav",
                            "size": 80.2
                          }
                        }
                      },
                      "ivr_menu_items": {
                        "data": []
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/ivr-work-times/21\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"date\": {\n      \"mon\": \"08:00-20:00\",\n      \"tue\": \"08:00-20:00\",\n      \"wed\": \"08:00-20:00\",\n      \"thu\": \"08:00-20:00\",\n      \"fri\": \"08:00-20:00\",\n      \"sat\": \"10:00-16:00\"\n    },\n    \"hello_audios\": [\n      101,\n      104\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-work-times/21\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"date\": {\n        \"mon\": \"08:00-20:00\",\n        \"tue\": \"08:00-20:00\",\n        \"wed\": \"08:00-20:00\",\n        \"thu\": \"08:00-20:00\",\n        \"fri\": \"08:00-20:00\",\n        \"sat\": \"10:00-16:00\"\n      },\n      \"hello_audios\": [\n        101,\n        104\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-work-times/21\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"date\":{\"mon\":\"08:00-20:00\",\"tue\":\"08:00-20:00\",\"wed\":\"08:00-20:00\",\"thu\":\"08:00-20:00\",\"fri\":\"08:00-20:00\",\"sat\":\"10:00-16:00\"},\"hello_audios\":[101,104]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteIvrWorkTime",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Delete an IVR working-time block",
        "description": "Deletes a working-time block. Its menu items are not deleted automatically. The dialplan is not regenerated by this call — update the IVR (`PUT /ivrs/{id}`) afterwards to apply the change to routing.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes the record permanently.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Working-time block id.",
            "schema": {
              "type": "integer",
              "example": 21
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted block.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/IvrWorkTime"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 21,
                    "attributes": {
                      "type": "any",
                      "date": null,
                      "use_global_redirect": true,
                      "createdAt": "2026-01-15T10:05:00.000Z",
                      "updatedAt": "2026-01-15T10:05:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/ivr-work-times/21\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-work-times/21\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-work-times/21\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/ivr-menu-items": {
      "get": {
        "operationId": "listIvrMenuItems",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "List IVR menu items",
        "description": "Returns IVR menu items. Each item is a digit of a menu (`playback.extension` or `redirect.extension` is the digit the caller presses) and does one of two things: `playback` — plays an audio file and opens a sub-menu (`childs`), or `redirect` — sends the call to an employee (`user`), a group (`group`) or hangs up (`call_end`). `parent` / `childs` describe the nesting; `uuid` is the client-side id you sent on creation.\nAlways filter by the working-time block: `filters[ivr_work_time][id][$eq]=21&populate=*` (deep: `populate[redirect][populate]=*&populate[playback][populate]=*&populate[parent]=true&populate[childs]=true`).\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of menu items.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Menu items.",
                      "items": {
                        "$ref": "#/components/schemas/IvrMenuItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 301,
                      "attributes": {
                        "uuid": "a1",
                        "createdAt": "2026-01-15T10:10:00.000Z",
                        "updatedAt": "2026-01-15T10:10:00.000Z",
                        "playback": null,
                        "redirect": {
                          "id": 51,
                          "call_end": null,
                          "extension": "1",
                          "group": {
                            "data": {
                              "id": 5,
                              "attributes": {
                                "name": "Sales",
                                "number": "001"
                              }
                            }
                          },
                          "user": {
                            "data": null
                          }
                        },
                        "parent": {
                          "data": null
                        },
                        "childs": {
                          "data": []
                        },
                        "ivr_work_time": {
                          "data": {
                            "id": 21,
                            "attributes": {
                              "type": "byDates"
                            }
                          }
                        }
                      }
                    },
                    {
                      "id": 302,
                      "attributes": {
                        "uuid": "a2",
                        "createdAt": "2026-01-15T10:10:00.000Z",
                        "updatedAt": "2026-01-15T10:10:00.000Z",
                        "playback": {
                          "id": 52,
                          "extension": "2",
                          "playback_audio": {
                            "data": {
                              "id": 105,
                              "attributes": {
                                "name": "Support menu",
                                "type": "sound",
                                "category": "additional",
                                "url": "/uploads/sound/company_1/support.wav",
                                "size": 60.1
                              }
                            }
                          }
                        },
                        "redirect": null,
                        "parent": {
                          "data": null
                        },
                        "childs": {
                          "data": [
                            {
                              "id": 303,
                              "attributes": {
                                "uuid": "a2-1"
                              }
                            }
                          ]
                        },
                        "ivr_work_time": {
                          "data": {
                            "id": 21,
                            "attributes": {
                              "type": "byDates"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 2
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/ivr-menu-items\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-menu-items\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-menu-items\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "saveIvrMenuItems",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Save the whole menu of a working-time block",
        "description": "Replaces the complete menu tree of one working-time block in a single call. This is NOT a single-item create: the body `data` is an object whose keys are `\"0\"`, `\"1\"`, … (one entry per menu item, flat list, any depth); all items must have the same `ivr_work_time`. All existing items of the block (taken from item `\"0\"`) are deleted first, then the new items are created and the Asterisk dialplan of the parent IVR is regenerated synchronously.\nItem fields: `id` — your own string id (stored as `uuid`); `parent` — `id` of the parent item or `null` for top level; `position` — the digit the caller presses (saved as `extension`); `levels` — array describing the nesting path, its length is the depth and is checked against the tariff limit `maxIVRSubVoutines`; exactly one of `playback` (`{ \"playback_audio\": { \"id\": N } }` — play audio and open sub-menu) or `redirect` (`{ \"user\": {\"id\": N} }` — employee, `{ \"group\": {\"id\": N} }` — group, `{ \"call_end\": true }` — hang up). Send `redirect: {}` together with `playback` if you have no redirect; `redirect` must not be omitted when `playback` is null.\nNote: `redirect.ivr` (jump to another IVR) exists in the data model but is ignored by this endpoint.\nAn empty `data` (`{}`) deletes nothing and returns an empty object.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes all existing menu items of the working-time block and recreates them; regenerates the IVR dialplan in Asterisk (routing changes immediately).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Map of menu items keyed by `\"0\"`, `\"1\"`, … (flat list of the whole tree).",
                    "additionalProperties": {
                      "$ref": "#/components/schemas/IvrMenuItemInput"
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "0": {
                    "id": "a1",
                    "ivr_work_time": 21,
                    "parent": null,
                    "position": 1,
                    "levels": [
                      1
                    ],
                    "playback": null,
                    "redirect": {
                      "group": {
                        "id": 5
                      }
                    }
                  },
                  "1": {
                    "id": "a2",
                    "ivr_work_time": 21,
                    "parent": null,
                    "position": 2,
                    "levels": [
                      2
                    ],
                    "playback": {
                      "playback_audio": {
                        "id": 105
                      }
                    },
                    "redirect": {}
                  },
                  "2": {
                    "id": "a2-1",
                    "ivr_work_time": 21,
                    "parent": "a2",
                    "position": 1,
                    "levels": [
                      2,
                      1
                    ],
                    "playback": null,
                    "redirect": {
                      "user": {
                        "id": 44
                      }
                    }
                  },
                  "3": {
                    "id": "a2-2",
                    "ivr_work_time": 21,
                    "parent": "a2",
                    "position": 0,
                    "levels": [
                      2,
                      0
                    ],
                    "playback": null,
                    "redirect": {
                      "call_end": true
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The parent IVR with its working-time blocks and the saved menu items (nested relations are populated).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Ivr"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 7,
                    "attributes": {
                      "name": "Main menu",
                      "number": "001",
                      "hold_time": 5,
                      "max_input": 3,
                      "redirection": "group",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-16T09:30:00.000Z",
                      "domain": {
                        "data": {
                          "id": 12,
                          "attributes": {
                            "name": "examplellc"
                          }
                        }
                      },
                      "ivr_work_times": {
                        "data": [
                          {
                            "id": 21,
                            "attributes": {
                              "type": "byDates",
                              "ivr_menu_items": {
                                "data": [
                                  {
                                    "id": 310,
                                    "attributes": {
                                      "uuid": "a1",
                                      "redirect": {
                                        "extension": "1",
                                        "group": {
                                          "data": {
                                            "id": 5,
                                            "attributes": {
                                              "name": "Sales",
                                              "number": "001"
                                            }
                                          }
                                        }
                                      },
                                      "parent": {
                                        "data": null
                                      },
                                      "childs": {
                                        "data": []
                                      }
                                    }
                                  },
                                  {
                                    "id": 311,
                                    "attributes": {
                                      "uuid": "a2",
                                      "playback": {
                                        "extension": "2",
                                        "playback_audio": {
                                          "data": {
                                            "id": 105,
                                            "attributes": {
                                              "name": "Support menu",
                                              "url": "/uploads/sound/company_1/support.wav"
                                            }
                                          }
                                        }
                                      },
                                      "parent": {
                                        "data": null
                                      },
                                      "childs": {
                                        "data": [
                                          {
                                            "id": 312,
                                            "attributes": {
                                              "uuid": "a2-1"
                                            }
                                          },
                                          {
                                            "id": 313,
                                            "attributes": {
                                              "uuid": "a2-2"
                                            }
                                          }
                                        ]
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Tariff limit on menu depth exceeded: `Максимальна кількість вложеностей для вашого тарифу 3` (an item's `levels.length` is greater than the tariff's `maxIVRSubVoutines`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Максимальна кількість вложеностей для вашого тарифу 3",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/ivr-menu-items\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"0\": {\n      \"id\": \"a1\",\n      \"ivr_work_time\": 21,\n      \"parent\": null,\n      \"position\": 1,\n      \"levels\": [\n        1\n      ],\n      \"playback\": null,\n      \"redirect\": {\n        \"group\": {\n          \"id\": 5\n        }\n      }\n    },\n    \"1\": {\n      \"id\": \"a2\",\n      \"ivr_work_time\": 21,\n      \"parent\": null,\n      \"position\": 2,\n      \"levels\": [\n        2\n      ],\n      \"playback\": {\n        \"playback_audio\": {\n          \"id\": 105\n        }\n      },\n      \"redirect\": {}\n    },\n    \"2\": {\n      \"id\": \"a2-1\",\n      \"ivr_work_time\": 21,\n      \"parent\": \"a2\",\n      \"position\": 1,\n      \"levels\": [\n        2,\n        1\n      ],\n      \"playback\": null,\n      \"redirect\": {\n        \"user\": {\n          \"id\": 44\n        }\n      }\n    },\n    \"3\": {\n      \"id\": \"a2-2\",\n      \"ivr_work_time\": 21,\n      \"parent\": \"a2\",\n      \"position\": 0,\n      \"levels\": [\n        2,\n        0\n      ],\n      \"playback\": null,\n      \"redirect\": {\n        \"call_end\": true\n      }\n    }\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-menu-items\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"0\": {\n        \"id\": \"a1\",\n        \"ivr_work_time\": 21,\n        \"parent\": null,\n        \"position\": 1,\n        \"levels\": [\n          1\n        ],\n        \"playback\": null,\n        \"redirect\": {\n          \"group\": {\n            \"id\": 5\n          }\n        }\n      },\n      \"1\": {\n        \"id\": \"a2\",\n        \"ivr_work_time\": 21,\n        \"parent\": null,\n        \"position\": 2,\n        \"levels\": [\n          2\n        ],\n        \"playback\": {\n          \"playback_audio\": {\n            \"id\": 105\n          }\n        },\n        \"redirect\": {}\n      },\n      \"2\": {\n        \"id\": \"a2-1\",\n        \"ivr_work_time\": 21,\n        \"parent\": \"a2\",\n        \"position\": 1,\n        \"levels\": [\n          2,\n          1\n        ],\n        \"playback\": null,\n        \"redirect\": {\n          \"user\": {\n            \"id\": 44\n          }\n        }\n      },\n      \"3\": {\n        \"id\": \"a2-2\",\n        \"ivr_work_time\": 21,\n        \"parent\": \"a2\",\n        \"position\": 0,\n        \"levels\": [\n          2,\n          0\n        ],\n        \"playback\": null,\n        \"redirect\": {\n          \"call_end\": true\n        }\n      }\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-menu-items\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"0\":{\"id\":\"a1\",\"ivr_work_time\":21,\"parent\":null,\"position\":1,\"levels\":[1],\"playback\":null,\"redirect\":{\"group\":{\"id\":5}}},\"1\":{\"id\":\"a2\",\"ivr_work_time\":21,\"parent\":null,\"position\":2,\"levels\":[2],\"playback\":{\"playback_audio\":{\"id\":105}},\"redirect\":{}},\"2\":{\"id\":\"a2-1\",\"ivr_work_time\":21,\"parent\":\"a2\",\"position\":1,\"levels\":[2,1],\"playback\":null,\"redirect\":{\"user\":{\"id\":44}}},\"3\":{\"id\":\"a2-2\",\"ivr_work_time\":21,\"parent\":\"a2\",\"position\":0,\"levels\":[2,0],\"playback\":null,\"redirect\":{\"call_end\":true}}}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/ivr-menu-items/{id}": {
      "get": {
        "operationId": "getIvrMenuItem",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Get an IVR menu item",
        "description": "Returns one menu item by id. Use `populate` to include `playback`, `redirect`, `parent`, `childs`, `ivr_work_time`.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Menu item id.",
            "schema": {
              "type": "integer",
              "example": 301
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Menu item.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/IvrMenuItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 301,
                    "attributes": {
                      "uuid": "a1",
                      "createdAt": "2026-01-15T10:10:00.000Z",
                      "updatedAt": "2026-01-15T10:10:00.000Z",
                      "playback": null,
                      "redirect": {
                        "id": 51,
                        "call_end": null,
                        "extension": "1"
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/ivr-menu-items/301\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-menu-items/301\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-menu-items/301\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteIvrMenuItem",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Delete an IVR menu item (not functional)",
        "deprecated": true,
        "description": "Intended to delete a single menu item, but in the current implementation the pre-check rejects every request with `Can`t delete ivr menu item with childs in` (the check never passes) and the handler itself is empty. Do not use. To remove items, resend the whole menu with `POST /ivr-menu-items` without them.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None (the request is always rejected).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Menu item id.",
            "schema": {
              "type": "integer",
              "example": 301
            }
          }
        ],
        "responses": {
          "400": {
            "description": "Always returned: `Can`t delete ivr menu item with childs in`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Can`t delete ivr menu item with childs in",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/ivr-menu-items/301\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/ivr-menu-items/301\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/ivr-menu-items/301\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/groups": {
      "get": {
        "operationId": "listGroups",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "List call groups",
        "description": "Returns the call groups (Asterisk queues) of the company. Two modes:\n1. Default — search index (Meilisearch): parameters `page`, `pageSize`, `search` (matches `name` or the internal SIP address `inner_number`), `order_by` (`id` | `employees_count`), `order_direction` (`asc` | `desc`). Extra computed fields are returned: `employees_count`, `inner_number`. The index is updated asynchronously, so a just-created group may appear with a short delay.\n2. `strapiSearch=true` — direct database query with standard Strapi parameters (`pagination[*]`, `sort`, `filters`, `populate`); `meta` is empty in this mode. Useful `populate`: `domain`, `group_employees`, `audio_musiconhold`, `in_phones`, `out_phone`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "strapiSearch",
            "in": "query",
            "required": false,
            "description": "`true` — query the database with Strapi parameters instead of the search index.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ],
              "example": "true"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Search-index mode: page number (default 1).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Search-index mode: page size (default 10).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 10,
              "example": 10
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Search-index mode: exact match by group name or internal SIP address.",
            "schema": {
              "type": "string",
              "example": "Sales"
            }
          },
          {
            "name": "order_by",
            "in": "query",
            "required": false,
            "description": "Search-index mode: sort field.",
            "schema": {
              "type": "string",
              "enum": [
                "id",
                "employees_count"
              ],
              "default": "id",
              "example": "employees_count"
            }
          },
          {
            "name": "order_direction",
            "in": "query",
            "required": false,
            "description": "Search-index mode: sort direction.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc",
              "example": "desc"
            }
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of groups.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Groups.",
                      "items": {
                        "$ref": "#/components/schemas/Group"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 5,
                      "attributes": {
                        "name": "Sales",
                        "number": "001",
                        "algoritm": "to_all",
                        "redirect": false,
                        "to_anwer_remain": false,
                        "queue_number": false,
                        "timeout": 90,
                        "maxlen": 0,
                        "servicelevel": 30,
                        "wrapuptime": 5,
                        "anounceHoldTime": "no",
                        "retry": 5,
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z",
                        "domain": {
                          "data": {
                            "id": 12,
                            "attributes": {
                              "name": "examplellc"
                            }
                          }
                        },
                        "group_employees": {
                          "data": [
                            {
                              "id": 71,
                              "attributes": {
                                "penalty": 1
                              }
                            }
                          ]
                        },
                        "employees_count": 1,
                        "inner_number": "2001@examplellc.sip.onevoiplanet.com"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`Service error` — the search index is unavailable; retry with `strapiSearch=true`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Service error",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/groups\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/groups\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/groups\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createGroup",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Create a call group",
        "description": "Creates a call group (queue) in one of the company's SIP domains. Body: `{ \"data\": {...} }`; `name`, `domain` and `algoritm` are required. The internal 3-digit queue number (`number`) is generated automatically if omitted (first free `001`–`999` in the domain). `company` is set from the token.\nMembers are not added here: use `POST /group-employee/bulk-create` (or `POST /group-employees`). If `group_employees` is sent in the body it is ignored, except an empty array which is rejected.\nThe queue is created in Asterisk asynchronously by a background job right after the record is saved. To receive calls, route a phone number to the group through the Numbers API (`addInPhone`).\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Creates the queue in Asterisk (background job).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/GroupInput"
                      }
                    ],
                    "description": "Group fields (Strapi envelope). Required: `name`, `domain`, `algoritm`."
                  }
                }
              },
              "example": {
                "data": {
                  "name": "Sales",
                  "domain": 12,
                  "algoritm": "uniform",
                  "timeout": 60,
                  "retry": 5,
                  "wrapuptime": 5,
                  "maxlen": 0,
                  "queue_number": true,
                  "anounceHoldTime": "once",
                  "audio_musiconhold": 110
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created group.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 5,
                    "attributes": {
                      "name": "Sales",
                      "number": "001",
                      "algoritm": "uniform",
                      "redirect": false,
                      "to_anwer_remain": false,
                      "queue_number": true,
                      "timeout": 60,
                      "maxlen": 0,
                      "servicelevel": null,
                      "wrapuptime": 5,
                      "anounceHoldTime": "once",
                      "retry": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Messages: `Domain required!`, `Domain not found`, `Group already exist!` (same name in the domain), `Audio validation failed!` (audio file not of the company), `group_employees is empty`, `algoritm is a required field`, `Всі группи зайняті в данному домені` (no free queue number).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Group already exist!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/groups\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"name\": \"Sales\",\n    \"domain\": 12,\n    \"algoritm\": \"uniform\",\n    \"timeout\": 60,\n    \"retry\": 5,\n    \"wrapuptime\": 5,\n    \"maxlen\": 0,\n    \"queue_number\": true,\n    \"anounceHoldTime\": \"once\",\n    \"audio_musiconhold\": 110\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/groups\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"name\": \"Sales\",\n      \"domain\": 12,\n      \"algoritm\": \"uniform\",\n      \"timeout\": 60,\n      \"retry\": 5,\n      \"wrapuptime\": 5,\n      \"maxlen\": 0,\n      \"queue_number\": true,\n      \"anounceHoldTime\": \"once\",\n      \"audio_musiconhold\": 110\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/groups\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"name\":\"Sales\",\"domain\":12,\"algoritm\":\"uniform\",\"timeout\":60,\"retry\":5,\"wrapuptime\":5,\"maxlen\":0,\"queue_number\":true,\"anounceHoldTime\":\"once\",\"audio_musiconhold\":110}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/groups/{id}": {
      "get": {
        "operationId": "getGroup",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Get a call group",
        "description": "Returns one group of the company. Returns `data: null` if the group does not exist or belongs to another company. Use `populate` for `domain`, `group_employees`, audio files, `in_phones`, `out_phone`.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Group id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Group, or `data: null` when not found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 5,
                    "attributes": {
                      "name": "Sales",
                      "number": "001",
                      "algoritm": "uniform",
                      "redirect": false,
                      "to_anwer_remain": false,
                      "queue_number": true,
                      "timeout": 60,
                      "maxlen": 0,
                      "servicelevel": 30,
                      "wrapuptime": 5,
                      "anounceHoldTime": "once",
                      "retry": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z",
                      "domain": {
                        "data": {
                          "id": 12,
                          "attributes": {
                            "name": "examplellc"
                          }
                        }
                      },
                      "group_employees": {
                        "data": [
                          {
                            "id": 71,
                            "attributes": {
                              "penalty": 1
                            }
                          },
                          {
                            "id": 72,
                            "attributes": {
                              "penalty": 2
                            }
                          }
                        ]
                      },
                      "audio_musiconhold": {
                        "data": {
                          "id": 110,
                          "attributes": {
                            "name": "Hold music",
                            "type": "moh",
                            "category": "additional",
                            "url": "/uploads/moh/company_1/hold.wav",
                            "size": 1500.3
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/groups/5\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/groups/5\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/groups/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateGroup",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Update a call group",
        "description": "Updates group settings. Body: `{ \"data\": {...} }`. `domain` cannot be changed (removed from the body); `group_employees` is ignored (empty array is rejected) — manage members through the group-employee endpoints.\nBefore saving, the current members are re-synchronised with the queue; if `algoritm` changes, member penalties are recalculated (`consistent` → 1, 2, 3…; other strategies → all 1). The Asterisk queue parameters are updated asynchronously by a background job.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Rewrites the queue members and queue parameters in Asterisk (background job).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Group id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/GroupInput"
                      }
                    ],
                    "description": "Fields to update (Strapi envelope). `domain` is ignored."
                  }
                }
              },
              "example": {
                "data": {
                  "name": "Sales (EU)",
                  "algoritm": "consistent",
                  "timeout": 45
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated group.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 5,
                    "attributes": {
                      "name": "Sales (EU)",
                      "number": "001",
                      "algoritm": "consistent",
                      "redirect": false,
                      "to_anwer_remain": false,
                      "queue_number": true,
                      "timeout": 45,
                      "maxlen": 0,
                      "servicelevel": 30,
                      "wrapuptime": 5,
                      "anounceHoldTime": "once",
                      "retry": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-17T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `Audio validation failed!`, `group_employees is empty`, or a Strapi field error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Audio validation failed!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/groups/5\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"name\": \"Sales (EU)\",\n    \"algoritm\": \"consistent\",\n    \"timeout\": 45\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/groups/5\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"name\": \"Sales (EU)\",\n      \"algoritm\": \"consistent\",\n      \"timeout\": 45\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/groups/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"name\":\"Sales (EU)\",\"algoritm\":\"consistent\",\"timeout\":45}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteGroup",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Delete a call group",
        "description": "Deletes a group of the company together with its memberships. Refused while a phone number is routed to the group (incoming or outgoing) or while a callback/recall widget redirects to it. The queue is removed from Asterisk asynchronously by a background job.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes the group and its memberships permanently; removes the queue from Asterisk (background job).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Group id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted group.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 5,
                    "attributes": {
                      "name": "Sales",
                      "number": "001",
                      "algoritm": "uniform",
                      "redirect": false,
                      "to_anwer_remain": false,
                      "queue_number": true,
                      "timeout": 60,
                      "maxlen": 0,
                      "servicelevel": 30,
                      "wrapuptime": 5,
                      "anounceHoldTime": "once",
                      "retry": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Group is still in use: `Detach phones before remove group` (a number is routed to the group; `details.data` holds the group with its phones) or `Detach widgets before remove group` (`details.recallWidgetName` / `details.callbackWidgetName`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Detach widgets before remove group",
                    "details": {
                      "callbackWidgetName": [
                        "Website widget"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "description": "Returned when the group does not exist or belongs to another company (the ownership check fails with an internal error instead of 404).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 500,
                    "name": "InternalServerError",
                    "message": "Internal Server Error",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/groups/5\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/groups/5\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/groups/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/group-employees": {
      "get": {
        "operationId": "listGroupEmployees",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "List group members",
        "description": "Returns group memberships (employee ↔ group with a `penalty` priority) for all groups of the company. Filter by group: `filters[group][id][$eq]=5`. Useful `populate`: `user` (employee), `group`. An employee can be a member of only one group.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of memberships.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Memberships.",
                      "items": {
                        "$ref": "#/components/schemas/GroupEmployee"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 71,
                      "attributes": {
                        "penalty": 1,
                        "createdAt": "2026-01-15T10:20:00.000Z",
                        "updatedAt": "2026-01-15T10:20:00.000Z",
                        "group": {
                          "data": {
                            "id": 5,
                            "attributes": {
                              "name": "Sales",
                              "number": "001",
                              "algoritm": "uniform"
                            }
                          }
                        },
                        "user": {
                          "data": {
                            "id": 44,
                            "attributes": {
                              "username": "john.doe",
                              "email": "john.doe@example.com",
                              "firstName": "John",
                              "lastName": "Doe"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/group-employees\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employees\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employees\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createGroupEmployee",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Add an employee to a group",
        "description": "Adds one employee (`user`) of the company to a group of the company. Body: `{ \"data\": { \"group\": 5, \"user\": 44, \"penalty\": 1 } }`. `penalty` below 2 is stored as 1. An employee may belong to one group only.\nThe member is added to the Asterisk queue asynchronously (background job) and the group's phone states are refreshed. The employee must have a SIP line, otherwise the queue member is not created.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Adds the member to the Asterisk queue (background job).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/GroupEmployeeInput"
                      }
                    ],
                    "description": "Membership fields (Strapi envelope). Required: `group`, `user`."
                  }
                }
              },
              "example": {
                "data": {
                  "group": 5,
                  "user": 44,
                  "penalty": 1
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created membership.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GroupEmployee"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 71,
                    "attributes": {
                      "penalty": 1,
                      "createdAt": "2026-01-15T10:20:00.000Z",
                      "updatedAt": "2026-01-15T10:20:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `Empty group`, `Group not found` (also when the group belongs to another company), `User not found` (also when the user belongs to another company), `User already added to group`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "User already added to group",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/group-employees\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"group\": 5,\n    \"user\": 44,\n    \"penalty\": 1\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employees\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"group\": 5,\n      \"user\": 44,\n      \"penalty\": 1\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employees\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"group\":5,\"user\":44,\"penalty\":1}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/group-employees/{id}": {
      "get": {
        "operationId": "getGroupEmployee",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Get a group membership",
        "description": "Returns one membership of the company's groups. Returns `data: null` if not found or if the group belongs to another company.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Membership (group-employee) id.",
            "schema": {
              "type": "integer",
              "example": 71
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Membership, or `data: null`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GroupEmployee"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 71,
                    "attributes": {
                      "penalty": 1,
                      "createdAt": "2026-01-15T10:20:00.000Z",
                      "updatedAt": "2026-01-15T10:20:00.000Z",
                      "user": {
                        "data": {
                          "id": 44,
                          "attributes": {
                            "username": "john.doe",
                            "email": "john.doe@example.com",
                            "firstName": "John",
                            "lastName": "Doe"
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/group-employees/71\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employees/71\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employees/71\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateGroupEmployee",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Change a member's priority",
        "description": "Updates the `penalty` (priority) of a membership. Only `penalty` is taken from the body; values below 2 are stored as 1. The group cannot be changed — delete and re-add the member instead.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None beyond the record (the queue member penalty in Asterisk is not updated by this call).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Membership (group-employee) id.",
            "schema": {
              "type": "integer",
              "example": 71
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Fields to update (Strapi envelope).",
                    "properties": {
                      "penalty": {
                        "type": "integer",
                        "minimum": 1,
                        "description": "Priority inside the queue (1 = highest; used by the `consistent` strategy).",
                        "example": 2
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "penalty": 2
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated membership.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GroupEmployee"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 71,
                    "attributes": {
                      "penalty": 2,
                      "createdAt": "2026-01-15T10:20:00.000Z",
                      "updatedAt": "2026-01-17T12:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Not found` — the membership does not exist or belongs to another company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/group-employees/71\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"penalty\": 2\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employees/71\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"penalty\": 2\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employees/71\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"penalty\":2}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteGroupEmployee",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Remove an employee from a group",
        "description": "Deletes a membership of the company's groups. The member is removed from the Asterisk queue asynchronously (background job) and the group's phone states are refreshed.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes the membership permanently; removes the member from the Asterisk queue (background job).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Membership (group-employee) id.",
            "schema": {
              "type": "integer",
              "example": 71
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted membership.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GroupEmployee"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 71,
                    "attributes": {
                      "penalty": 1,
                      "createdAt": "2026-01-15T10:20:00.000Z",
                      "updatedAt": "2026-01-15T10:20:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Not found` — the membership does not exist or belongs to another company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/group-employees/71\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employees/71\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employees/71\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/group-employee/bulk-create": {
      "post": {
        "operationId": "bulkCreateGroupEmployees",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Set the members of a group",
        "description": "Replaces the whole member list of one group. Body: `{ \"data\": { \"group_employees\": [ { \"group\": 5, \"user\": 44 }, { \"group\": 5, \"user\": 45 } ] } }` — all entries must reference the same group (the group of the first entry is used). Existing memberships of that group are deleted, then the new ones are created. `penalty` is assigned automatically from the group's `algoritm`: `consistent` → 1, 2, 3… in list order; all other strategies → 1.\nThe Asterisk queue members are rewritten synchronously as part of the request. Employees must have SIP lines. Sending an empty `group_employees` array fails with 500.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes all current memberships of the group and recreates them; rewrites the queue members in Asterisk.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupEmployeesBulkInput"
              },
              "example": {
                "data": {
                  "group_employees": [
                    {
                      "group": 5,
                      "user": 44
                    },
                    {
                      "group": 5,
                      "user": 45
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result lists. `failed` is always empty in the current implementation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GroupEmployeesBulkResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "success": [
                      {
                        "group": 5,
                        "user": 44,
                        "penalty": 1
                      },
                      {
                        "group": 5,
                        "user": 45,
                        "penalty": 1
                      }
                    ],
                    "failed": []
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `Employees not found` (the company has no employees), `Group not found` (also for a group of another company).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Group not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/group-employee/bulk-create\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"group_employees\": [\n      {\n        \"group\": 5,\n        \"user\": 44\n      },\n      {\n        \"group\": 5,\n        \"user\": 45\n      }\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employee/bulk-create\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"group_employees\": [\n        {\n          \"group\": 5,\n          \"user\": 44\n        },\n        {\n          \"group\": 5,\n          \"user\": 45\n        }\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employee/bulk-create\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"group_employees\":[{\"group\":5,\"user\":44},{\"group\":5,\"user\":45}]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/group-employee/bulk-update": {
      "put": {
        "operationId": "bulkUpdateGroupEmployees",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Replace the members of a group",
        "description": "Same as `POST /group-employee/bulk-create` (replaces the whole member list of the group and rewrites the Asterisk queue members), but an empty `group_employees` array is rejected with a 400 instead of failing.\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes all current memberships of the group and recreates them; rewrites the queue members in Asterisk.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupEmployeesBulkInput"
              },
              "example": {
                "data": {
                  "group_employees": [
                    {
                      "group": 5,
                      "user": 44
                    },
                    {
                      "group": 5,
                      "user": 46
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result lists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GroupEmployeesBulkResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "success": [
                      {
                        "group": 5,
                        "user": 44,
                        "penalty": 1
                      },
                      {
                        "group": 5,
                        "user": 46,
                        "penalty": 2
                      }
                    ],
                    "failed": []
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `group_employees is empty`, `Employees not found`, `Group not found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "group_employees is empty",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/group-employee/bulk-update\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"group_employees\": [\n      {\n        \"group\": 5,\n        \"user\": 44\n      },\n      {\n        \"group\": 5,\n        \"user\": 46\n      }\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employee/bulk-update\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"group_employees\": [\n        {\n          \"group\": 5,\n          \"user\": 44\n        },\n        {\n          \"group\": 5,\n          \"user\": 46\n        }\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employee/bulk-update\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"group_employees\":[{\"group\":5,\"user\":44},{\"group\":5,\"user\":46}]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/group-employee/bulk-delete": {
      "delete": {
        "operationId": "bulkDeleteGroupEmployees",
        "tags": [
          "IVR & Groups"
        ],
        "summary": "Remove several members from a group",
        "description": "Deletes several memberships of ONE group by id. Ids are passed in the query string as an array: `?ids[0]=71&ids[1]=72` (a single `ids=71` value is not accepted — always use the array form). Only memberships that belong to the same group as the first id and to the company are deleted; the others are returned in `failed`.\nQuirk: the request must also carry a JSON body with a `data` object (e.g. `{ \"data\": {} }`), otherwise the pre-check fails with 500.\nMemberships are deleted directly; the queue members in Asterisk are removed for the affected group.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Deletes memberships permanently; removes the members from the Asterisk queue.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "description": "Membership ids to delete, array form: `ids[0]=71&ids[1]=72`.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "example": [
                71,
                72
              ]
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Must be present (may be empty) — required by the pre-check."
                  }
                }
              },
              "example": {
                "data": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted ids (`success`) and skipped ids (`failed`, as strings).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Result.",
                      "properties": {
                        "success": {
                          "type": "array",
                          "description": "Ids of deleted memberships.",
                          "items": {
                            "type": "integer"
                          }
                        },
                        "failed": {
                          "type": "array",
                          "description": "Ids that were skipped (another group or another company).",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "success": [
                      71,
                      72
                    ],
                    "failed": [
                      "99"
                    ]
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Not found` — none of the ids exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/group-employee/bulk-delete?ids=71%2C72\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {}\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/group-employee/bulk-delete?ids=71%2C72\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {}\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/group-employee/bulk-delete?ids=71%2C72\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user-notifications/inbox/summary": {
      "get": {
        "operationId": "notifInboxSummary",
        "tags": [
          "Notifications"
        ],
        "summary": "Unread counters for the bell icon",
        "description": "Returns the number of unread personal notifications (push notifications sent to this user) and unread news\n(published announcements visible to the user, hidden ones excluded). Poll it to render the badge.\n\n\n**Required role:** Employee, Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Counters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Counters.",
                      "properties": {
                        "personalUnread": {
                          "type": "integer",
                          "description": "Unread personal notifications.",
                          "example": 3
                        },
                        "newsUnread": {
                          "type": "integer",
                          "description": "Unread news.",
                          "example": 1
                        },
                        "hasUnread": {
                          "type": "boolean",
                          "description": "Any unread.",
                          "example": true
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "personalUnread": 3,
                    "newsUnread": 1,
                    "hasUnread": true
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/user-notifications/inbox/summary\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user-notifications/inbox/summary\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user-notifications/inbox/summary\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user-notifications/inbox/personal": {
      "get": {
        "operationId": "notifPersonalInbox",
        "tags": [
          "Notifications"
        ],
        "summary": "Personal notifications inbox",
        "description": "Returns the user's personal notifications (the same events that are sent as push: order status, expiring numbers,\nlow balance, missed calls, …), newest first, both as a flat `items` list and grouped by day in `groups`.\nPagination uses plain `page` / `pageSize` query parameters (default 1 / 20).\n`payload` carries event-specific fields (orderId, orderStatus, amount, phone, subscriptionId, redirect_link, …) for deep links.\n\n\n**Required role:** Employee, Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number (from 1).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Page size (default 20).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "example": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Notifications.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Items and groups.",
                      "properties": {
                        "groups": {
                          "type": "array",
                          "description": "Items grouped by calendar day (UTC), newest day first.",
                          "items": {
                            "type": "object",
                            "description": "Day group.",
                            "properties": {
                              "date": {
                                "type": "string",
                                "format": "date",
                                "description": "Day (YYYY-MM-DD).",
                                "example": "2026-02-02"
                              },
                              "items": {
                                "type": "array",
                                "description": "Notifications of the day.",
                                "items": {
                                  "$ref": "#/components/schemas/NotifPersonalItem"
                                }
                              }
                            }
                          }
                        },
                        "items": {
                          "type": "array",
                          "description": "Flat page of notifications.",
                          "items": {
                            "$ref": "#/components/schemas/NotifPersonalItem"
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "groups": [
                      {
                        "date": "2026-02-02",
                        "items": [
                          {
                            "id": 501,
                            "serviceName": "fcm",
                            "isSend": true,
                            "isRead": false,
                            "readAt": null,
                            "createdAt": "2026-02-02T07:00:05.193Z",
                            "updatedAt": "2026-02-02T07:00:05.193Z",
                            "templateName": "expiresPhoneAfter3Days",
                            "title": "Number 380441234567 expires in 3 days",
                            "description": "Top up 229 ₴ to renew the number",
                            "payload": {
                              "templateName": "expiresPhoneAfter3Days",
                              "title": "Number 380441234567 expires in 3 days",
                              "description": "Top up 229 ₴ to renew the number",
                              "amount": 229,
                              "phone": "380441234567"
                            }
                          }
                        ]
                      }
                    ],
                    "items": [
                      {
                        "id": 501,
                        "serviceName": "fcm",
                        "isSend": true,
                        "isRead": false,
                        "readAt": null,
                        "createdAt": "2026-02-02T07:00:05.193Z",
                        "updatedAt": "2026-02-02T07:00:05.193Z",
                        "templateName": "expiresPhoneAfter3Days",
                        "title": "Number 380441234567 expires in 3 days",
                        "description": "Top up 229 ₴ to renew the number",
                        "payload": {
                          "templateName": "expiresPhoneAfter3Days",
                          "title": "Number 380441234567 expires in 3 days",
                          "description": "Top up 229 ₴ to renew the number",
                          "amount": 229,
                          "phone": "380441234567"
                        }
                      }
                    ]
                  },
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 20,
                      "pageCount": 3,
                      "total": 59
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/user-notifications/inbox/personal\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user-notifications/inbox/personal\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user-notifications/inbox/personal\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user-notifications/inbox/personal/read": {
      "post": {
        "operationId": "notifPersonalMarkRead",
        "tags": [
          "Notifications"
        ],
        "summary": "Mark personal notifications as read",
        "description": "Marks the given personal notifications (`data.ids`) or all unread ones (`data.all: true`) as read.\nOnly the caller's own notifications are affected. Returns the number of updated rows.\n\n\n**Required role:** Employee, Company owner (approved)\n\n**Side effects:** Sets isRead/readAt on the notifications.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/NotifReadRequest"
                  }
                }
              },
              "example": {
                "data": {
                  "ids": [
                    501,
                    502
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Number of notifications marked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotifUpdatedResult"
                },
                "example": {
                  "data": {
                    "updated": 2
                  }
                }
              }
            }
          },
          "400": {
            "description": "Neither `all` nor a non-empty `ids`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "`ids` must be non-empty array",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/user-notifications/inbox/personal/read\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"ids\": [\n      501,\n      502\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user-notifications/inbox/personal/read\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"ids\": [\n        501,\n        502\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user-notifications/inbox/personal/read\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"ids\":[501,502]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user-notifications/inbox/news": {
      "get": {
        "operationId": "notifNewsInbox",
        "tags": [
          "Notifications"
        ],
        "summary": "News / announcements inbox",
        "description": "Returns published announcements from OneVOIPlanet addressed to everyone or to this user, newest first, with the\nuser's read/hidden state. Hidden items are omitted unless `includeHidden=true`.\nPagination uses plain `page` / `pageSize` (default 1 / 20). `body` is rich text (Markdown).\n\n\n**Required role:** Employee, Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number (from 1).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Page size (default 20).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "example": 20
            }
          },
          {
            "name": "includeHidden",
            "in": "query",
            "required": false,
            "description": "`true` — also return news the user has hidden.",
            "schema": {
              "type": "boolean",
              "default": false,
              "example": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "News.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Items and groups.",
                      "properties": {
                        "groups": {
                          "type": "array",
                          "description": "Items grouped by publication day (UTC).",
                          "items": {
                            "type": "object",
                            "description": "Day group.",
                            "properties": {
                              "date": {
                                "type": "string",
                                "format": "date",
                                "description": "Day (YYYY-MM-DD).",
                                "example": "2026-02-01"
                              },
                              "items": {
                                "type": "array",
                                "description": "News of the day.",
                                "items": {
                                  "$ref": "#/components/schemas/NotifNewsItem"
                                }
                              }
                            }
                          }
                        },
                        "items": {
                          "type": "array",
                          "description": "Flat page of news.",
                          "items": {
                            "$ref": "#/components/schemas/NotifNewsItem"
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "groups": [
                      {
                        "date": "2026-02-01",
                        "items": [
                          {
                            "id": 5,
                            "title": "New eSIM bundles",
                            "description": "Regional eSIM bundles are now available.",
                            "body": "Regional **eSIM** bundles are now available in the cabinet.",
                            "image": null,
                            "publishedAt": "2026-02-01T14:12:28.987Z",
                            "createdAt": "2026-02-01T14:12:27.857Z",
                            "updatedAt": "2026-02-01T14:12:28.992Z",
                            "isRead": false,
                            "readAt": null,
                            "isHidden": false,
                            "hiddenAt": null
                          }
                        ]
                      }
                    ],
                    "items": [
                      {
                        "id": 5,
                        "title": "New eSIM bundles",
                        "description": "Regional eSIM bundles are now available.",
                        "body": "Regional **eSIM** bundles are now available in the cabinet.",
                        "image": null,
                        "publishedAt": "2026-02-01T14:12:28.987Z",
                        "createdAt": "2026-02-01T14:12:27.857Z",
                        "updatedAt": "2026-02-01T14:12:28.992Z",
                        "isRead": false,
                        "readAt": null,
                        "isHidden": false,
                        "hiddenAt": null
                      }
                    ]
                  },
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 20,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/user-notifications/inbox/news\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user-notifications/inbox/news/read": {
      "post": {
        "operationId": "notifNewsMarkRead",
        "tags": [
          "Notifications"
        ],
        "summary": "Mark news as read",
        "description": "Marks the given news (`data.ids`) or all visible non-hidden news (`data.all: true`) as read for the caller.\nUnknown ids are silently counted. Returns the number of items processed.\n\n\n**Required role:** Employee, Company owner (approved)\n\n**Side effects:** Stores the per-user read state of the news.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/NotifReadRequest"
                  }
                }
              },
              "example": {
                "data": {
                  "all": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Number of news marked (0 if nothing matched).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotifUpdatedResult"
                },
                "example": {
                  "data": {
                    "updated": 1
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/user-notifications/inbox/news/read\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"all\": true\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news/read\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"all\": true\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news/read\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"all\":true}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user-notifications/inbox/news/hide": {
      "post": {
        "operationId": "notifNewsHide",
        "tags": [
          "Notifications"
        ],
        "summary": "Hide news items",
        "description": "Hides the given news for the caller (they disappear from `/inbox/news` unless `includeHidden=true` and stop\ncounting as unread). Pass `data.ids` — a non-empty array of news ids.\n\n\n**Required role:** Employee, Company owner (approved)\n\n**Side effects:** Stores the per-user hidden state of the news.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "ids"
                    ],
                    "description": "Ids to hide.",
                    "properties": {
                      "ids": {
                        "type": "array",
                        "items": {
                          "type": "integer"
                        },
                        "description": "News ids.",
                        "example": [
                          5
                        ]
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "ids": [
                    5
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Number of hidden news.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotifHiddenResult"
                },
                "example": {
                  "data": {
                    "hidden": 1
                  }
                }
              }
            }
          },
          "400": {
            "description": "`ids` missing or empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "`ids` must be non-empty array",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/user-notifications/inbox/news/hide\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"ids\": [\n      5\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news/hide\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"ids\": [\n        5\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news/hide\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"ids\":[5]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/user-notifications/inbox/news/clear": {
      "post": {
        "operationId": "notifNewsClear",
        "tags": [
          "Notifications"
        ],
        "summary": "Hide all news",
        "description": "Hides every currently visible news item for the caller (\"clear all\"). No body required.\n\n\n**Required role:** Employee, Company owner (approved)\n\n**Side effects:** Stores the hidden state for all visible news.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Number of hidden news.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotifHiddenResult"
                },
                "example": {
                  "data": {
                    "hidden": 4
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/user-notifications/inbox/news/clear\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news/clear\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/user-notifications/inbox/news/clear\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/notification-settings": {
      "get": {
        "operationId": "notifSettingsList",
        "tags": [
          "Notifications"
        ],
        "summary": "Notification settings of the company",
        "description": "Core Strapi list filtered to the caller's company (middleware adds `filters[company][id]`), so at most one record\nis returned: contacts (e-mail / Telegram) with per-topic flags, daily digest time and the low-balance threshold.\nUse `populate=notification_contacts` to include the contacts.\n\n**Known issue:** at the moment the server rejects this request with `400 \"Invalid parameter company\"` for regular\nusers (the company filter added by the middleware is not accepted). Until fixed, read the settings from\n`GET /companies/my` → `notification_setting`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Settings of the company (0 or 1 record).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Settings records.",
                      "items": {
                        "$ref": "#/components/schemas/NotifSetting"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 11,
                      "attributes": {
                        "send_notification_at": "09:00:00.000",
                        "minimum_balance": 100,
                        "notified_minimum_balance": null,
                        "is_minimum_balance_notified": false,
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-02-01T10:00:00.000Z",
                        "notification_contacts": [
                          {
                            "id": 1,
                            "type": "email",
                            "contact": "owner@example.com",
                            "finance": true,
                            "call": true,
                            "widgets": true,
                            "statistics": true,
                            "lang": "en",
                            "tgUsername": null,
                            "tgFirstname": null
                          }
                        ]
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Known issue: company filter rejected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Invalid parameter company",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/notification-settings\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/notification-settings\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/notification-settings\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "notifSettingsCreate",
        "tags": [
          "Notifications"
        ],
        "summary": "Create notification settings",
        "description": "Creates the notification settings record of the company (one per company; `company` is attached automatically\nand any `company` in the body is removed). Strapi envelope `{ \"data\": {...} }`.\nEach contact is an e-mail or a Telegram chat id with flags of what to receive: `finance` (balance, payments),\n`call` (missed calls), `widgets` (callback-widget requests), `statistics` (daily statistics digest).\nContacts must be unique. For contacts with `widgets: true` a daily digest is scheduled at `send_notification_at` (Kyiv time).\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** Creates the settings; schedules daily widget digest jobs for contacts with `widgets: true`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/NotifSettingInput"
                  }
                }
              },
              "example": {
                "data": {
                  "send_notification_at": "09:00:00",
                  "minimum_balance": 100,
                  "notification_contacts": [
                    {
                      "type": "email",
                      "contact": "owner@example.com",
                      "finance": true,
                      "call": true,
                      "widgets": true,
                      "statistics": true,
                      "lang": "en"
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NotifSetting"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 11,
                    "attributes": {
                      "send_notification_at": "09:00:00.000",
                      "minimum_balance": 100,
                      "notified_minimum_balance": null,
                      "is_minimum_balance_notified": null,
                      "createdAt": "2026-02-02T09:00:00.000Z",
                      "updatedAt": "2026-02-02T09:00:00.000Z",
                      "notification_contacts": [
                        {
                          "id": 1,
                          "type": "email",
                          "contact": "owner@example.com",
                          "finance": true,
                          "call": true,
                          "widgets": true,
                          "statistics": true,
                          "lang": "en",
                          "tgUsername": null,
                          "tgFirstname": null
                        }
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Duplicate contacts or validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Emails not unique",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/notification-settings\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"send_notification_at\": \"09:00:00\",\n    \"minimum_balance\": 100,\n    \"notification_contacts\": [\n      {\n        \"type\": \"email\",\n        \"contact\": \"owner@example.com\",\n        \"finance\": true,\n        \"call\": true,\n        \"widgets\": true,\n        \"statistics\": true,\n        \"lang\": \"en\"\n      }\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/notification-settings\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"send_notification_at\": \"09:00:00\",\n      \"minimum_balance\": 100,\n      \"notification_contacts\": [\n        {\n          \"type\": \"email\",\n          \"contact\": \"owner@example.com\",\n          \"finance\": true,\n          \"call\": true,\n          \"widgets\": true,\n          \"statistics\": true,\n          \"lang\": \"en\"\n        }\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/notification-settings\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"send_notification_at\":\"09:00:00\",\"minimum_balance\":100,\"notification_contacts\":[{\"type\":\"email\",\"contact\":\"owner@example.com\",\"finance\":true,\"call\":true,\"widgets\":true,\"statistics\":true,\"lang\":\"en\"}]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/notification-settings/{id}": {
      "get": {
        "operationId": "notifSettingsGet",
        "tags": [
          "Notifications"
        ],
        "summary": "Get notification settings by id",
        "description": "Core Strapi read of the settings record, restricted to the caller's company by middleware.\n**Known issue:** currently answers `400 \"Invalid parameter company\"` (see `GET /notification-settings`);\nuse `GET /companies/my` → `notification_setting` instead.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Settings id.",
            "schema": {
              "type": "integer",
              "example": 11
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NotifSetting"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 11,
                    "attributes": {
                      "send_notification_at": "09:00:00.000",
                      "minimum_balance": 100,
                      "notified_minimum_balance": null,
                      "is_minimum_balance_notified": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z",
                      "notification_contacts": [
                        {
                          "id": 1,
                          "type": "email",
                          "contact": "owner@example.com",
                          "finance": true,
                          "call": true,
                          "widgets": true,
                          "statistics": true,
                          "lang": "en",
                          "tgUsername": null,
                          "tgFirstname": null
                        }
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Known issue: company filter rejected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Invalid parameter company",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/notification-settings/11\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/notification-settings/11\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/notification-settings/11\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "notifSettingsUpdate",
        "tags": [
          "Notifications"
        ],
        "summary": "Update notification settings",
        "description": "Updates the settings record (Strapi envelope `{ \"data\": {...} }`). `notification_contacts` is a repeatable\ncomponent: send the **full** list — contacts without `id` are created, existing ones keep their `id`, omitted ones\nare removed. Contacts must be unique. Pending widget-digest jobs are re-scheduled according to the new contacts/time.\nUse the id of your company's record (`GET /companies/my` → `notification_setting.data.id`).\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** Updates the settings; cancels pending widget digest jobs and schedules new ones.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Settings id.",
            "schema": {
              "type": "integer",
              "example": 11
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/NotifSettingInput"
                  }
                }
              },
              "example": {
                "data": {
                  "minimum_balance": 200,
                  "notification_contacts": [
                    {
                      "id": 1,
                      "type": "email",
                      "contact": "owner@example.com",
                      "finance": true,
                      "call": true,
                      "widgets": false,
                      "statistics": true,
                      "lang": "en"
                    },
                    {
                      "type": "telegram",
                      "contact": "123456789",
                      "finance": true,
                      "call": false,
                      "widgets": true,
                      "statistics": false,
                      "lang": "uk-UA"
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NotifSetting"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 11,
                    "attributes": {
                      "send_notification_at": "09:00:00.000",
                      "minimum_balance": 200,
                      "notified_minimum_balance": null,
                      "is_minimum_balance_notified": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-02T09:00:00.000Z",
                      "notification_contacts": [
                        {
                          "id": 1,
                          "type": "email",
                          "contact": "owner@example.com",
                          "finance": true,
                          "call": true,
                          "widgets": false,
                          "statistics": true,
                          "lang": "en",
                          "tgUsername": null,
                          "tgFirstname": null
                        },
                        {
                          "id": 2,
                          "type": "telegram",
                          "contact": "123456789",
                          "finance": true,
                          "call": false,
                          "widgets": true,
                          "statistics": false,
                          "lang": "uk-UA",
                          "tgUsername": null,
                          "tgFirstname": null
                        }
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Duplicate contacts or validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Emails not unique",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/notification-settings/11\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"minimum_balance\": 200,\n    \"notification_contacts\": [\n      {\n        \"id\": 1,\n        \"type\": \"email\",\n        \"contact\": \"owner@example.com\",\n        \"finance\": true,\n        \"call\": true,\n        \"widgets\": false,\n        \"statistics\": true,\n        \"lang\": \"en\"\n      },\n      {\n        \"type\": \"telegram\",\n        \"contact\": \"123456789\",\n        \"finance\": true,\n        \"call\": false,\n        \"widgets\": true,\n        \"statistics\": false,\n        \"lang\": \"uk-UA\"\n      }\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/notification-settings/11\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"minimum_balance\": 200,\n      \"notification_contacts\": [\n        {\n          \"id\": 1,\n          \"type\": \"email\",\n          \"contact\": \"owner@example.com\",\n          \"finance\": true,\n          \"call\": true,\n          \"widgets\": false,\n          \"statistics\": true,\n          \"lang\": \"en\"\n        },\n        {\n          \"type\": \"telegram\",\n          \"contact\": \"123456789\",\n          \"finance\": true,\n          \"call\": false,\n          \"widgets\": true,\n          \"statistics\": false,\n          \"lang\": \"uk-UA\"\n        }\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/notification-settings/11\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"minimum_balance\":200,\"notification_contacts\":[{\"id\":1,\"type\":\"email\",\"contact\":\"owner@example.com\",\"finance\":true,\"call\":true,\"widgets\":false,\"statistics\":true,\"lang\":\"en\"},{\"type\":\"telegram\",\"contact\":\"123456789\",\"finance\":true,\"call\":false,\"widgets\":true,\"statistics\":false,\"lang\":\"uk-UA\"}]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "notifSettingsDelete",
        "tags": [
          "Notifications"
        ],
        "summary": "Delete notification settings",
        "description": "Core Strapi delete of the settings record. After deletion the company receives no e-mail/Telegram notifications\nuntil a new record is created with `POST /notification-settings`.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the settings and all notification contacts.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Settings id.",
            "schema": {
              "type": "integer",
              "example": 11
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted record.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NotifSetting"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 11,
                    "attributes": {
                      "send_notification_at": "09:00:00.000",
                      "minimum_balance": 100,
                      "notified_minimum_balance": null,
                      "is_minimum_balance_notified": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/notification-settings/11\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/notification-settings/11\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/notification-settings/11\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/firebase-tokens": {
      "post": {
        "operationId": "notifPushTokenRegister",
        "tags": [
          "Notifications"
        ],
        "summary": "Register a push (FCM) token for this device",
        "description": "Registers the device's Firebase Cloud Messaging token for the authenticated user so the device receives\nnotifications. Call it after login and whenever FCM rotates the token. Strapi envelope `{ \"data\": {...} }`;\n`user` is set from the JWT. The token must look like an FCM token (100–4096 chars containing `:`).\nIf the token already exists it is re-bound to the current user and `200 { data: { id, attributes: { token } } }`\nis returned. A user keeps at most 4 tokens — the oldest are removed automatically.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** Stores the token; may delete the user's oldest tokens (keeps 4); re-binds an existing token to the caller.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "token"
                    ],
                    "description": "Token data.",
                    "properties": {
                      "token": {
                        "type": "string",
                        "description": "FCM registration token.",
                        "example": "cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789"
                      },
                      "device_os": {
                        "type": "string",
                        "enum": [
                          "android",
                          "ios",
                          "web"
                        ],
                        "description": "Platform.",
                        "example": "ios"
                      },
                      "device_id": {
                        "type": "string",
                        "description": "Stable device identifier (used to de-duplicate pushes per device).",
                        "example": "0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B"
                      },
                      "user_label": {
                        "type": "string",
                        "description": "Free-form device label.",
                        "example": "John's iPhone"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "token": "cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789",
                  "device_os": "ios",
                  "device_id": "0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B",
                  "user_label": "John's iPhone"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token stored (full record), or `{ id, attributes: { token } }` when it already existed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NotifPushToken"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "examples": {
                  "created": {
                    "summary": "New token",
                    "value": {
                      "data": {
                        "id": 845,
                        "attributes": {
                          "token": "cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789",
                          "user_label": "John's iPhone",
                          "device_os": "ios",
                          "device_id": "0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B",
                          "createdAt": "2026-02-02T09:00:00.000Z",
                          "updatedAt": "2026-02-02T09:00:00.000Z"
                        }
                      },
                      "meta": {}
                    }
                  },
                  "existing": {
                    "summary": "Token already registered",
                    "value": {
                      "data": {
                        "id": 845,
                        "attributes": {
                          "token": "cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Token has an invalid format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "BadRequestError",
                    "message": "Invalid FCM token format",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/firebase-tokens\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"token\": \"cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789\",\n    \"device_os\": \"ios\",\n    \"device_id\": \"0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B\",\n    \"user_label\": \"John'\\''s iPhone\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/firebase-tokens\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"token\": \"cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789\",\n      \"device_os\": \"ios\",\n      \"device_id\": \"0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B\",\n      \"user_label\": \"John's iPhone\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/firebase-tokens\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"token\":\"cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789\",\"device_os\":\"ios\",\"device_id\":\"0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B\",\"user_label\":\"John's iPhone\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/firebase-tokens/{id}": {
      "delete": {
        "operationId": "notifPushTokenDelete",
        "tags": [
          "Notifications"
        ],
        "summary": "Unregister a push token (on logout)",
        "description": "Deletes the FCM token of the current user. **The path parameter is the token string itself**, not the record id\n(URL-encode it). Only a token belonging to the caller is deleted; otherwise `null` is returned.\nCall it before `POST /auth/logout` so the device stops receiving pushes.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** Deletes the token record.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FCM token string (URL-encoded).",
            "schema": {
              "type": "string",
              "example": "cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted record (flat) or null if not found for this user.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/NotifPushTokenFlat"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "example": {
                  "id": 845,
                  "token": "cXYZ123_example-installation-id:APA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789",
                  "user_label": "John's iPhone",
                  "device_os": "ios",
                  "device_id": "0F1E2D3C-4B5A-6978-8A9B-0C1D2E3F4A5B",
                  "createdAt": "2026-02-02T09:00:00.000Z",
                  "updatedAt": "2026-02-02T09:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/firebase-tokens/cXYZ123_example-installation-id%3AAPA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/firebase-tokens/cXYZ123_example-installation-id%3AAPA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/firebase-tokens/cXYZ123_example-installation-id%3AAPA91bExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBodyExampleTokenBody0123456789\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/voip-push/update-registration": {
      "post": {
        "operationId": "notifVoipPushRegister",
        "tags": [
          "Notifications"
        ],
        "summary": "Register / unregister a VoIP push token (incoming calls)",
        "description": "Registers the device token used to wake the app on an **incoming call** to the user's SIP line\n(APNs VoIP push for iOS, FCM for Android/web), or removes it (`register: false`).\nStrapi envelope `{ \"data\": {...} }`. The token is bound to the SIP line of the authenticated user — the user\nmust have a SIP line, otherwise the request fails with 500.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** Stores / removes the VoIP push token in the call-signalling cache.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "token",
                      "device",
                      "register"
                    ],
                    "description": "Registration data.",
                    "properties": {
                      "token": {
                        "type": "string",
                        "description": "APNs VoIP token (iOS) or FCM token (android/web).",
                        "example": "a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90"
                      },
                      "device": {
                        "type": "string",
                        "enum": [
                          "ios",
                          "android",
                          "web"
                        ],
                        "description": "Platform.",
                        "example": "ios"
                      },
                      "register": {
                        "type": "boolean",
                        "description": "true — register, false — remove.",
                        "example": true
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "token": "a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90",
                  "device": "ios",
                  "register": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokenRegistered": {
                      "type": "boolean",
                      "description": "Echo of `register`: true — registered, false — removed.",
                      "example": true
                    }
                  }
                },
                "example": {
                  "tokenRegistered": true
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "device must match the following: \"/(web|ios|android)/\"",
                    "details": {
                      "errors": [
                        {
                          "path": [
                            "device"
                          ],
                          "message": "device must match the following: \"/(web|ios|android)/\"",
                          "name": "ValidationError"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/voip-push/update-registration\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"token\": \"a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90\",\n    \"device\": \"ios\",\n    \"register\": true\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/voip-push/update-registration\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"token\": \"a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90\",\n      \"device\": \"ios\",\n      \"register\": true\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/voip-push/update-registration\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"token\":\"a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90\",\"device\":\"ios\",\"register\":true}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phones": {
      "get": {
        "operationId": "listMarketplacePhones",
        "tags": [
          "Numbers"
        ],
        "summary": "Numbers available for purchase",
        "description": "Catalogue of free numbers (the marketplace): only numbers that are not assigned to any company and not reserved\n(`reserved: false`) are returned. It does **not** return the numbers of your company — use `GET /companies/my` with\n`populate[phones]` or `GET /companies/phone/{id}` for that.\n\nStandard Strapi `filters`, `sort` and `populate` are ignored by this method; use the specific query parameters\nbelow. The response always includes `country_prefix`, `phone_prefix` (with `phone_prefix_owner`, its prices and\nlocalized names) and the per-number `prices`. `fullnumber` is the full number in E.164 without the leading plus\n(e.g. `380441234567`). The purchase itself is done through Orders.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Currency id: return only numbers that have a price in this currency.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          },
          {
            "name": "phonesNumber",
            "in": "query",
            "required": false,
            "description": "Substring search in `fullnumber` (digits only).",
            "schema": {
              "type": "string",
              "example": "4412"
            }
          },
          {
            "name": "phonesCity",
            "in": "query",
            "required": false,
            "description": "Id of the prefix owner (city/operator, `phone_prefix_owner`). Alias: `phonesOperator`.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "phonesOperator",
            "in": "query",
            "required": false,
            "description": "Same as `phonesCity` (used when `phonesCity` is absent).",
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "phonesType",
            "in": "query",
            "required": false,
            "description": "Number class.",
            "schema": {
              "type": "string",
              "enum": [
                "simple",
                "bronze",
                "silver",
                "golden",
                "platinum",
                "exclusive"
              ],
              "example": "simple"
            }
          },
          {
            "name": "tab",
            "in": "query",
            "required": false,
            "description": "Prefix owner type: `all` (default), `0800` — national (toll-free), `urban` — city, `mobiles` — mobile.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "0800",
                "urban",
                "mobiles"
              ],
              "example": "urban"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of free numbers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Numbers.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersMarketplacePhone"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 101,
                      "attributes": {
                        "number": "1234567",
                        "channels": 4,
                        "type": "simple",
                        "reserved": false,
                        "verificationRequired": false,
                        "verifiedAt": null,
                        "fullnumber": "380441234567",
                        "priority": 3,
                        "prefix_type": "urban",
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z",
                        "company": {
                          "data": null
                        },
                        "country_prefix": {
                          "data": {
                            "id": 1,
                            "attributes": {
                              "country": "UA",
                              "iso": "UA",
                              "prefix": "380",
                              "isAvailable": true,
                              "outgoing_calls": true,
                              "incoming_calls": true,
                              "outgoing_sms": false,
                              "incoming_sms": true
                            }
                          }
                        },
                        "phone_prefix": {
                          "data": {
                            "id": 9,
                            "attributes": {
                              "prefix": "44",
                              "capacity": null,
                              "validation": false,
                              "call": "INT",
                              "incoming_sms": false,
                              "outgoing_sms": false,
                              "phone_prefix_owner": {
                                "data": {
                                  "id": 1,
                                  "attributes": {
                                    "owner": "м. Київ",
                                    "type": "city",
                                    "available_call_directions": "both",
                                    "prices": {
                                      "data": []
                                    },
                                    "names": [
                                      {
                                        "id": 1,
                                        "locales": "uk-UA",
                                        "name": "м. Київ"
                                      },
                                      {
                                        "id": 2,
                                        "locales": "en",
                                        "name": "Kyiv"
                                      }
                                    ]
                                  }
                                }
                              }
                            }
                          }
                        },
                        "prices": {
                          "data": [
                            {
                              "id": 55,
                              "attributes": {
                                "currency": {
                                  "data": {
                                    "id": 3,
                                    "attributes": {
                                      "name_short": "UAH",
                                      "name_long": "Гривня",
                                      "symbol": "₴"
                                    }
                                  }
                                },
                                "price_by_field": [
                                  {
                                    "id": 7,
                                    "name": "price",
                                    "price": 500,
                                    "isPriceStatic": false
                                  }
                                ]
                              }
                            }
                          ]
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 40,
                      "total": 1000
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/phones\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phones/{id}": {
      "get": {
        "operationId": "getPhone",
        "tags": [
          "Numbers"
        ],
        "summary": "Get a number of my company",
        "description": "Returns one number that belongs to the company of the current user. Numbers of other companies (and free\nmarketplace numbers) answer `404`. Relations are included only when requested via `populate`, e.g.\n`populate[0]=inSip&populate[1]=out_sip_lines&populate[2]=country_prefix&populate[3]=sip_trunk`.\n\nSpecial option `populate[balances]=true` adds the `balances` object with the remaining minutes of talk packets\nattached to the number, grouped by expiration date (empty object when the number has no packets).\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Number id.",
            "schema": {
              "type": "integer",
              "example": 35
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "The number.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NumbersPhone"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 35,
                    "attributes": {
                      "number": "1234567",
                      "channels": 4,
                      "type": "exclusive",
                      "reserved": false,
                      "verificationRequired": false,
                      "verifiedAt": null,
                      "fullnumber": "380441234567",
                      "priority": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z",
                      "country_prefix": {
                        "data": {
                          "id": 1,
                          "attributes": {
                            "country": "UA",
                            "iso": "UA",
                            "prefix": "380",
                            "isAvailable": true,
                            "outgoing_calls": true,
                            "incoming_calls": true,
                            "outgoing_sms": false,
                            "incoming_sms": true
                          }
                        }
                      },
                      "inSip": {
                        "data": {
                          "id": 12,
                          "attributes": {
                            "number": "010",
                            "password": "********",
                            "record_call": true,
                            "call_forwarding": "off",
                            "redirectActivity": false,
                            "redirectTo": null
                          }
                        }
                      },
                      "sip_trunk": {
                        "data": null
                      },
                      "balances": {
                        "2026-03-01T00:00:00Z": [
                          {
                            "side": "in",
                            "seconds_left": 54000,
                            "total": 60000
                          },
                          {
                            "side": "out",
                            "seconds_left": 1200,
                            "total": 6000
                          }
                        ]
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/phones/35\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones/35\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones/35\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updatePhone",
        "tags": [
          "Numbers"
        ],
        "summary": "Update number settings",
        "description": "Standard Strapi update: `{ \"data\": { ... } }`. Intended for the routing settings of a number: incoming line\n(`inSip` — SIP line, `in_group` — call group, `in_ivr_line` — IVR; only one of them should be set), outgoing lines\n(`out_sip_lines`, `out_group_lines`, `out_ivr_lines` — many-to-many, pass `{ \"connect\": [...], \"disconnect\": [...] }`\nor a full array of ids), `priority` and `channels`. For the validated variants use `POST /phones/addInPhone` and\n`POST /phones/addOutPhone`.\n\nThe class `type`, `number`, `fullnumber`, `reserved`, `verificationRequired`, `verifiedAt` and `company` are managed by the\nplatform — do not send them. Changing `company` is treated as attaching/detaching the number and triggers tariff\nre-provisioning and internal notifications.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Re-provisions call routing for the number in the telephony core (asynchronous job `kamailioPhoneLineAssign`); if the number is connected via a SIP trunk, the trunk registration is re-created. Changing `company` additionally changes tariff plans and sends an internal notification.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Number id.",
            "schema": {
              "type": "integer",
              "example": 35
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Fields to update (Strapi envelope).",
                    "properties": {
                      "channels": {
                        "type": "integer",
                        "description": "Number of simultaneous calls (channels).",
                        "example": 4
                      },
                      "priority": {
                        "type": "integer",
                        "description": "Display/selection priority of the number.",
                        "example": 1
                      },
                      "inSip": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "description": "Id of the SIP line that receives incoming calls, or null.",
                        "example": 12
                      },
                      "in_group": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "description": "Id of the call group that receives incoming calls, or null.",
                        "example": null
                      },
                      "in_ivr_line": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "description": "Id of the IVR that receives incoming calls, or null.",
                        "example": null
                      },
                      "out_sip_lines": {
                        "type": "object",
                        "description": "SIP lines that use this number as caller id for outgoing calls.",
                        "properties": {
                          "connect": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Ids to attach.",
                            "example": [
                              12,
                              13
                            ]
                          },
                          "disconnect": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Ids to detach.",
                            "example": []
                          }
                        }
                      },
                      "out_group_lines": {
                        "type": "object",
                        "description": "Call groups that use this number for outgoing calls.",
                        "properties": {
                          "connect": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Ids to attach.",
                            "example": []
                          },
                          "disconnect": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Ids to detach.",
                            "example": []
                          }
                        }
                      },
                      "out_ivr_lines": {
                        "type": "object",
                        "description": "IVR menus that use this number for outgoing calls.",
                        "properties": {
                          "connect": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Ids to attach.",
                            "example": []
                          },
                          "disconnect": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Ids to detach.",
                            "example": []
                          }
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "inSip": 12,
                  "out_sip_lines": {
                    "connect": [
                      12,
                      13
                    ],
                    "disconnect": []
                  },
                  "priority": 1
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated number.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NumbersPhone"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 35,
                    "attributes": {
                      "number": "1234567",
                      "channels": 4,
                      "type": "exclusive",
                      "reserved": false,
                      "verificationRequired": false,
                      "verifiedAt": null,
                      "fullnumber": "380441234567",
                      "priority": 1,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Missing `data` envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Missing \"data\" payload in the request body",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/phones/35\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"inSip\": 12,\n    \"out_sip_lines\": {\n      \"connect\": [\n        12,\n        13\n      ],\n      \"disconnect\": []\n    },\n    \"priority\": 1\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones/35\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"inSip\": 12,\n      \"out_sip_lines\": {\n        \"connect\": [\n          12,\n          13\n        ],\n        \"disconnect\": []\n      },\n      \"priority\": 1\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones/35\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"inSip\":12,\"out_sip_lines\":{\"connect\":[12,13],\"disconnect\":[]},\"priority\":1}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phones/addInPhone": {
      "post": {
        "operationId": "setPhoneIncomingLine",
        "tags": [
          "Numbers"
        ],
        "summary": "Set the incoming line of a number",
        "description": "Assigns where incoming calls to the number go: a SIP line (`sip` → `inSip`), a call group (`group` → `in_group`)\nor an IVR (`ivr` → `in_ivr_line`). Setting `sip` clears `in_group` and vice versa; setting `ivr` clears\n`out_ivr_lines`. The number must belong to your company. If the number already has an incoming line, the request\nmust `disconnect` it (its id must be present in `data[1].disconnect`), otherwise `Phone line already in use!`.\n\nBody: `phone` — number id, `data` — a 2-element array `[kind, relation]` where `relation` is a Strapi relation\npayload `{ \"connect\": [id], \"disconnect\": [id] }`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Re-provisions call routing for the number in the telephony core (asynchronous job).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NumbersLineAssignRequest"
              },
              "example": {
                "phone": 35,
                "data": [
                  "sip",
                  {
                    "connect": [
                      12
                    ],
                    "disconnect": [
                      11
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated number (flat object) with `inSip` (with `user`) and `in_group` populated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersPhoneFlat"
                },
                "example": {
                  "id": 35,
                  "number": "1234567",
                  "channels": 4,
                  "type": "exclusive",
                  "reserved": false,
                  "verificationRequired": false,
                  "verifiedAt": null,
                  "fullnumber": "380441234567",
                  "priority": 5,
                  "createdAt": "2026-01-15T10:00:00.000Z",
                  "updatedAt": "2026-02-01T10:00:00.000Z",
                  "inSip": {
                    "id": 12,
                    "number": "010",
                    "password": "********",
                    "record_call": true,
                    "call_forwarding": "off",
                    "user": {
                      "id": 7,
                      "username": "John Doe",
                      "email": "john@example.com"
                    }
                  },
                  "in_group": null
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Messages: `Fields: \\`data\\`, \\`phone\\` must be defined!`, `Data length must be 2!`, `Phones id\\`s not valid!` (number is not in your company), `Phone line already in use!`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Phone line already in use!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/phones/addInPhone\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"phone\": 35,\n  \"data\": [\n    \"sip\",\n    {\n      \"connect\": [\n        12\n      ],\n      \"disconnect\": [\n        11\n      ]\n    }\n  ]\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones/addInPhone\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"phone\": 35,\n    \"data\": [\n      \"sip\",\n      {\n        \"connect\": [\n          12\n        ],\n        \"disconnect\": [\n          11\n        ]\n      }\n    ]\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones/addInPhone\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"phone\":35,\"data\":[\"sip\",{\"connect\":[12],\"disconnect\":[11]}]}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phones/addOutPhone": {
      "post": {
        "operationId": "setPhoneOutgoingLines",
        "tags": [
          "Numbers"
        ],
        "summary": "Attach outgoing lines to a number",
        "description": "Sets which SIP lines (`sip` → `out_sip_lines`), call groups (`group` → `out_group_lines`) or IVR menus\n(`ivr` → `out_ivr_lines`) use this number as the caller id for outgoing calls. The number must belong to your\ncompany. Body format is the same as for `POST /phones/addInPhone`: `data = [kind, { connect, disconnect }]`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Re-provisions call routing for the number in the telephony core (asynchronous job).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NumbersLineAssignRequest"
              },
              "example": {
                "phone": 35,
                "data": [
                  "sip",
                  {
                    "connect": [
                      12,
                      13
                    ],
                    "disconnect": []
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated number (flat object) with `out_sip_lines` (with `user`) and `out_group_lines` populated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersPhoneFlat"
                },
                "example": {
                  "id": 35,
                  "number": "1234567",
                  "channels": 4,
                  "type": "exclusive",
                  "reserved": false,
                  "verificationRequired": false,
                  "verifiedAt": null,
                  "fullnumber": "380441234567",
                  "priority": 5,
                  "createdAt": "2026-01-15T10:00:00.000Z",
                  "updatedAt": "2026-02-01T10:00:00.000Z",
                  "out_sip_lines": [
                    {
                      "id": 12,
                      "number": "010",
                      "password": "********",
                      "record_call": true,
                      "call_forwarding": "off",
                      "user": {
                        "id": 7,
                        "username": "John Doe",
                        "email": "john@example.com"
                      }
                    }
                  ],
                  "out_group_lines": []
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Messages: `Fields: \\`data\\`, \\`phone\\` must be defined!`, `Data length must be 2!`, `Phones id\\`s not valid!`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Phones id`s not valid!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/phones/addOutPhone\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"phone\": 35,\n  \"data\": [\n    \"sip\",\n    {\n      \"connect\": [\n        12,\n        13\n      ],\n      \"disconnect\": []\n    }\n  ]\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones/addOutPhone\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"phone\": 35,\n    \"data\": [\n      \"sip\",\n      {\n        \"connect\": [\n          12,\n          13\n        ],\n        \"disconnect\": []\n      }\n    ]\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones/addOutPhone\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"phone\":35,\"data\":[\"sip\",{\"connect\":[12,13],\"disconnect\":[]}]}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phones/countByTypes": {
      "post": {
        "operationId": "countMarketplacePhonesByType",
        "tags": [
          "Numbers"
        ],
        "summary": "Count free numbers by class",
        "description": "Returns how many free (unassigned, not reserved) numbers of each class have a price in the currency of your\ncompany. Useful for marketplace tabs. The body is optional; any Strapi query (e.g. `filters`) passed in it is\nmerged into the search, e.g. `{ \"filters\": { \"phone_prefix\": { \"phone_prefix_owner\": 1 } } }`.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "description": "Additional Strapi filters merged into the search.",
                    "example": {
                      "phone_prefix": {
                        "phone_prefix_owner": 1
                      }
                    }
                  }
                }
              },
              "example": {
                "filters": {
                  "phone_prefix": {
                    "phone_prefix_owner": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Counts per class.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "simple": {
                      "type": "integer",
                      "description": "Simple numbers.",
                      "example": 120
                    },
                    "bronze": {
                      "type": "integer",
                      "description": "Bronze numbers.",
                      "example": 30
                    },
                    "silver": {
                      "type": "integer",
                      "description": "Silver numbers.",
                      "example": 12
                    },
                    "golden": {
                      "type": "integer",
                      "description": "Golden numbers.",
                      "example": 5
                    },
                    "platinum": {
                      "type": "integer",
                      "description": "Platinum numbers.",
                      "example": 2
                    },
                    "exclusive": {
                      "type": "integer",
                      "description": "Exclusive numbers.",
                      "example": 1
                    }
                  }
                },
                "example": {
                  "simple": 120,
                  "bronze": 30,
                  "silver": 12,
                  "golden": 5,
                  "platinum": 2,
                  "exclusive": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/phones/countByTypes\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"filters\": {\n    \"phone_prefix\": {\n      \"phone_prefix_owner\": 1\n    }\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones/countByTypes\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"filters\": {\n      \"phone_prefix\": {\n        \"phone_prefix_owner\": 1\n      }\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones/countByTypes\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"filters\":{\"phone_prefix\":{\"phone_prefix_owner\":1}}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phones/line-restrictions/{phone}": {
      "get": {
        "operationId": "getPhoneLineRestrictions",
        "tags": [
          "Numbers"
        ],
        "summary": "Check whether a number may take another call",
        "description": "Service endpoint used by the telephony core: compares the number of active dialogs on the number with its\n`channels` limit and answers whether one more call is allowed. `phone` is the number as dialled (E.164 without\nplus, e.g. `380441234567`); the value `0` and internal SIP usernames always get `can_call: \"true\"`.\nValues are strings, not booleans. No token required.\n\n\n**Required role:** Public, Company owner (approved)\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "phone",
            "in": "path",
            "required": true,
            "description": "Number in E.164 without plus.",
            "schema": {
              "type": "string",
              "example": "380441234567"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Decision.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "can_call": {
                      "type": "string",
                      "enum": [
                        "true",
                        "false"
                      ],
                      "description": "`\"true\"` — a call may be accepted, `\"false\"` — channel limit reached.",
                      "example": "true"
                    }
                  }
                },
                "example": {
                  "can_call": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/phones/line-restrictions/380441234567\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones/line-restrictions/380441234567\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones/line-restrictions/380441234567\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phone-prefixes": {
      "get": {
        "operationId": "listPhonePrefixes",
        "tags": [
          "Numbers"
        ],
        "summary": "Number prefixes catalogue",
        "description": "Standard Strapi list of number prefixes (city/operator codes) with prices and talk packets. Public, no token\nneeded. Each prefix belongs to a `phone_prefix_owner` (city, operator, national 0800 range...) which in turn\nbelongs to a `country_prefix`. Prices are in `prices[].price_by_field`: `price` — monthly fee, `setup_price` —\nconnection fee, `phone_channel_price` — extra channel, `sms_incoming_price`/`sms_outgoing_price` — SMS; one\n`prices` entry per currency. `validation: true` means the regulator requires identity verification before a\nnumber from this prefix can be used (see `GET /didww-verifications/requirements`).\n\nUseful: `populate[phone_prefix_owner][populate][0]=country_prefix&populate[phone_prefix_owner][populate][1]=names`,\n`populate[prices][populate]=*`, `populate[talk_packets][populate]=*`,\n`filters[phone_prefix_owner][country_prefix][iso][$eq]=UA`, `filters[prefix][$startsWith]=44`.\nExtra sort by price: `priceSort=price:asc` or `setup_price:desc` together with `priceSortCurrency=<currency id>`\n(prefixes without a price in that currency go last).\n\n\n**Required role:** Public, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          },
          {
            "name": "priceSort",
            "in": "query",
            "required": false,
            "description": "Sort by a price field: `price:asc|desc` or `setup_price:asc|desc`. Requires `priceSortCurrency`; replaces `sort`.",
            "schema": {
              "type": "string",
              "example": "price:asc"
            }
          },
          {
            "name": "priceSortCurrency",
            "in": "query",
            "required": false,
            "description": "Currency id whose price is used for `priceSort`.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of prefixes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Prefixes.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersPhonePrefix"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 9,
                      "attributes": {
                        "prefix": "44",
                        "capacity": null,
                        "validation": false,
                        "call": "INT",
                        "incoming_sms": false,
                        "outgoing_sms": false,
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z",
                        "phone_prefix_owner": {
                          "data": {
                            "id": 1,
                            "attributes": {
                              "owner": "м. Київ",
                              "type": "city",
                              "available_call_directions": "both",
                              "country_prefix": {
                                "data": {
                                  "id": 1,
                                  "attributes": {
                                    "country": "UA",
                                    "iso": "UA",
                                    "prefix": "380",
                                    "isAvailable": true,
                                    "outgoing_calls": true,
                                    "incoming_calls": true,
                                    "outgoing_sms": false,
                                    "incoming_sms": true
                                  }
                                }
                              },
                              "names": [
                                {
                                  "id": 1,
                                  "locales": "uk-UA",
                                  "name": "м. Київ"
                                },
                                {
                                  "id": 2,
                                  "locales": "en",
                                  "name": "Kyiv"
                                }
                              ]
                            }
                          }
                        },
                        "prices": {
                          "data": [
                            {
                              "id": 501,
                              "attributes": {
                                "description": "Virtual numbers price",
                                "currency": {
                                  "data": {
                                    "id": 3,
                                    "attributes": {
                                      "name_short": "UAH",
                                      "name_long": "Гривня",
                                      "symbol": "₴",
                                      "prefix": "",
                                      "suffix": ""
                                    }
                                  }
                                },
                                "price_by_field": [
                                  {
                                    "id": 1,
                                    "name": "price",
                                    "price": 229,
                                    "isPriceStatic": false
                                  },
                                  {
                                    "id": 2,
                                    "name": "setup_price",
                                    "price": 229,
                                    "isPriceStatic": false
                                  }
                                ]
                              }
                            }
                          ]
                        },
                        "talk_packets": {
                          "data": [
                            {
                              "id": 3,
                              "attributes": {
                                "type": "small",
                                "incomming_seconds_included": "60000",
                                "outgoing_seconds_included": "6000",
                                "prices": {
                                  "data": [
                                    {
                                      "id": 77,
                                      "attributes": {
                                        "currency": {
                                          "data": {
                                            "id": 3,
                                            "attributes": {
                                              "name_short": "UAH",
                                              "name_long": "Гривня",
                                              "symbol": "₴"
                                            }
                                          }
                                        },
                                        "price_by_field": [
                                          {
                                            "id": 9,
                                            "name": "price",
                                            "price": 150,
                                            "isPriceStatic": false
                                          }
                                        ]
                                      }
                                    }
                                  ]
                                }
                              }
                            }
                          ]
                        },
                        "validation_fileds": []
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 230,
                      "total": 5742
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/phone-prefixes\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phone-prefixes\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phone-prefixes\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phone-prefixes/{id}": {
      "get": {
        "operationId": "getPhonePrefix",
        "tags": [
          "Numbers"
        ],
        "summary": "Get a number prefix",
        "description": "One prefix by id. Public. Use `populate` to include the owner, country, prices and talk packets (see list method).\n\n**Required role:** Public, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Prefix id.",
            "schema": {
              "type": "integer",
              "example": 9
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "The prefix.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NumbersPhonePrefix"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 9,
                    "attributes": {
                      "prefix": "44",
                      "capacity": null,
                      "validation": false,
                      "call": "INT",
                      "incoming_sms": false,
                      "outgoing_sms": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/phone-prefixes/9\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phone-prefixes/9\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phone-prefixes/9\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/country-prefixes": {
      "get": {
        "operationId": "listCountryPrefixes",
        "tags": [
          "Numbers"
        ],
        "summary": "Countries and dialling codes",
        "description": "Standard Strapi list of countries supported by the platform with their dialling codes and capabilities\n(`isAvailable` — numbers can be bought, `outgoing_calls`, `incoming_calls`, `outgoing_sms`, `incoming_sms`).\nPublic. Useful: `filters[isAvailable][$eq]=true`, `sort=country:asc`, `populate[phone_prefix_owners]=*`.\n\n\n**Required role:** Public, Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of countries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Countries.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersCountryPrefix"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "attributes": {
                        "country": "UA",
                        "iso": "UA",
                        "prefix": "380",
                        "isAvailable": true,
                        "outgoing_calls": true,
                        "incoming_calls": true,
                        "outgoing_sms": false,
                        "incoming_sms": true,
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 4,
                      "total": 96
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/country-prefixes\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/country-prefixes\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/country-prefixes\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/country-prefixes/{id}": {
      "get": {
        "operationId": "getCountryPrefix",
        "tags": [
          "Numbers"
        ],
        "summary": "Get a country",
        "description": "One country by id. Public.\n\n**Required role:** Public, Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Country id.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "The country.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NumbersCountryPrefix"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "attributes": {
                      "country": "UA",
                      "iso": "UA",
                      "prefix": "380",
                      "isAvailable": true,
                      "outgoing_calls": true,
                      "incoming_calls": true,
                      "outgoing_sms": false,
                      "incoming_sms": true,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/country-prefixes/1\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/country-prefixes/1\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/country-prefixes/1\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/companies/phone/{id}": {
      "get": {
        "operationId": "getCompanyPhone",
        "tags": [
          "Numbers"
        ],
        "summary": "Get a number of my company (flat form)",
        "description": "Returns one number of the current user's company. Unlike `GET /phones/{id}`, populated relations are returned as\nplain objects/arrays (without the `data`/`attributes` wrapper). `populate` works, e.g.\n`populate[0]=country_prefix&populate[1]=inSip&populate[2]=out_sip_lines`. Numbers of other companies answer with\n`data: null`.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Number id.",
            "schema": {
              "type": "integer",
              "example": 35
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "The number, or `data: null` if it is not in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Number entity.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Number id.",
                          "example": 35
                        },
                        "attributes": {
                          "$ref": "#/components/schemas/NumbersPhoneFlat"
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 35,
                    "attributes": {
                      "number": "1234567",
                      "channels": 4,
                      "type": "exclusive",
                      "reserved": false,
                      "verificationRequired": false,
                      "verifiedAt": null,
                      "fullnumber": "380441234567",
                      "priority": 5,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T10:00:00.000Z",
                      "country_prefix": {
                        "id": 1,
                        "country": "UA",
                        "iso": "UA",
                        "prefix": "380",
                        "isAvailable": true,
                        "outgoing_calls": true,
                        "incoming_calls": true,
                        "outgoing_sms": false,
                        "incoming_sms": true
                      },
                      "inSip": {
                        "id": 12,
                        "number": "010",
                        "password": "********",
                        "record_call": true,
                        "call_forwarding": "off",
                        "redirectActivity": false,
                        "redirectTo": null
                      },
                      "out_sip_lines": [
                        {
                          "id": 13,
                          "number": "011",
                          "password": "********",
                          "record_call": true,
                          "call_forwarding": "off",
                          "redirectActivity": null,
                          "redirectTo": null
                        }
                      ]
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/companies/phone/35\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/companies/phone/35\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/companies/phone/35\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-catalog/public/numbers/countries": {
      "get": {
        "operationId": "catalogNumberCountries",
        "tags": [
          "Numbers"
        ],
        "summary": "International numbers: summary by country",
        "description": "Public price list of international virtual numbers (DIDWW catalogue): for every country — number of available\nprefix groups, number types, monthly price range (USD), whether numbers are in stock and whether owner\nverification is required (`none` — buy immediately, `all` — documents required everywhere, `partial` — both).\nPrices are the platform's selling prices; the connection fee is already included in the monthly price\n(`setup_from` is always 0). Data is refreshed on catalogue sync (`updatedAt`), responses are cached for 5 minutes\n(`Cache-Control: public, max-age=300`). No token required; limit 120 requests per minute per IP.\n\n\n**Required role:** no token required (public)\n\n**Side effects:** None",
        "security": [],
        "responses": {
          "200": {
            "description": "Summary by country.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "description": "Currency of all prices (always `USD`).",
                      "example": "USD"
                    },
                    "updatedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When the catalogue was last synchronised.",
                      "example": "2026-09-02T15:47:05.918Z"
                    },
                    "data": {
                      "type": "array",
                      "description": "Countries.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersCatalogCountry"
                      }
                    }
                  }
                },
                "example": {
                  "currency": "USD",
                  "updatedAt": "2026-09-02T15:47:05.918Z",
                  "data": [
                    {
                      "country": "United States",
                      "iso": "US",
                      "code": "1",
                      "groups": 320,
                      "types": [
                        "Local",
                        "Toll-free"
                      ],
                      "monthly_from": 3.99,
                      "monthly_to": 24.99,
                      "setup_from": 0,
                      "in_stock": true,
                      "verification": "partial",
                      "verification_groups": 12,
                      "monthly_from_no_verification": 3.99
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-catalog/public/numbers/countries\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-catalog/public/numbers/countries\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-catalog/public/numbers/countries\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-catalog/public/numbers": {
      "get": {
        "operationId": "catalogNumbers",
        "tags": [
          "Numbers"
        ],
        "summary": "International numbers: prefix groups with prices",
        "description": "Public paginated list of international number groups (country + prefix/area) with the monthly price in USD,\nfeatures (`voice_in`, `voice_out`, `sms_in`, `sms_out`, `t38`), stock flag and whether owner verification is\nrequired. Filter by country (ISO-2 or exact country name), type, feature, verification and search; sort by\n`country`, `prefix` or `price`. No token required; limit 120 requests per minute per IP; cached 5 minutes.\n\n\n**Required role:** no token required (public)\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "ISO-2 country code (e.g. `US`) or exact country name from the summary.",
            "schema": {
              "type": "string",
              "example": "US"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Number type as returned in `type` (e.g. `Local`, `National`, `Mobile`, `Toll-free`).",
            "schema": {
              "type": "string",
              "example": "Local"
            }
          },
          {
            "name": "feature",
            "in": "query",
            "required": false,
            "description": "Required feature: `voice`, `sms`, `t38` (alias `fax`).",
            "schema": {
              "type": "string",
              "enum": [
                "voice",
                "sms",
                "t38",
                "fax"
              ],
              "example": "sms"
            }
          },
          {
            "name": "verification",
            "in": "query",
            "required": false,
            "description": "`none` — only groups that can be bought without documents; `required` — only groups that need owner verification.",
            "schema": {
              "type": "string",
              "enum": [
                "none",
                "required"
              ],
              "example": "none"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Digits — prefix starts with; text — country/area/city name contains.",
            "schema": {
              "type": "string",
              "example": "New York"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort field (default `country`).",
            "schema": {
              "type": "string",
              "enum": [
                "country",
                "prefix",
                "price"
              ],
              "example": "price"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "description": "Sort direction (default `asc`).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "example": "asc"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, from 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Page size (1–200, default 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "example": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of number groups.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "description": "Currency of all prices (always `USD`).",
                      "example": "USD"
                    },
                    "updatedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Last catalogue sync.",
                      "example": "2026-09-02T15:47:05.918Z"
                    },
                    "meta": {
                      "type": "object",
                      "description": "Pagination.",
                      "properties": {
                        "page": {
                          "type": "integer",
                          "description": "Current page.",
                          "example": 1
                        },
                        "pageSize": {
                          "type": "integer",
                          "description": "Page size.",
                          "example": 50
                        },
                        "total": {
                          "type": "integer",
                          "description": "Total groups.",
                          "example": 5612
                        }
                      }
                    },
                    "data": {
                      "type": "array",
                      "description": "Number groups.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersCatalogGroup"
                      }
                    }
                  }
                },
                "example": {
                  "currency": "USD",
                  "updatedAt": "2026-09-02T15:47:05.918Z",
                  "meta": {
                    "page": 1,
                    "pageSize": 50,
                    "total": 5612
                  },
                  "data": [
                    {
                      "id": "0f1e2d3c-0000-4000-8000-000000000001",
                      "country": "United States",
                      "iso": "US",
                      "code": "1",
                      "prefix": "212",
                      "area": "New York",
                      "city": "New York",
                      "region": "NY",
                      "type": "Local",
                      "features": [
                        "voice_in",
                        "voice_out",
                        "sms_in"
                      ],
                      "monthly": 3.99,
                      "setup": 0,
                      "in_stock": true,
                      "needs_verification": false
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-catalog/public/numbers\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-catalog/public/numbers\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-catalog/public/numbers\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-catalog/public/rates/countries": {
      "get": {
        "operationId": "catalogRateCountries",
        "tags": [
          "Numbers"
        ],
        "summary": "Outgoing call rates: summary by country",
        "description": "Public summary of per-minute rates for outgoing calls by destination country: number of prefixes and the\nprice range in **US cents per minute**. `route=international` (default) — calls from abroad; `local` — calls\noriginating in the same country; `all` — both. No token required; limit 120 requests per minute per IP; cached 5 minutes.\n\n\n**Required role:** no token required (public)\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "route",
            "in": "query",
            "required": false,
            "description": "Route type (default `international`).",
            "schema": {
              "type": "string",
              "enum": [
                "international",
                "local",
                "all"
              ],
              "example": "international"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Summary by country.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "description": "Currency (always `USD`).",
                      "example": "USD"
                    },
                    "unit": {
                      "type": "string",
                      "description": "Price unit (always `cent_per_minute`).",
                      "example": "cent_per_minute"
                    },
                    "route": {
                      "type": "string",
                      "description": "Applied route filter.",
                      "example": "international"
                    },
                    "updatedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Last rates import.",
                      "example": "2026-09-30T17:12:39.461Z"
                    },
                    "data": {
                      "type": "array",
                      "description": "Countries.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersCatalogRateCountry"
                      }
                    }
                  }
                },
                "example": {
                  "currency": "USD",
                  "unit": "cent_per_minute",
                  "route": "international",
                  "updatedAt": "2026-09-30T17:12:39.461Z",
                  "data": [
                    {
                      "country": "United States",
                      "iso": "US",
                      "prefixes": 25,
                      "price_from": 2,
                      "price_to": 15
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-catalog/public/rates/countries\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-catalog/public/rates/countries\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-catalog/public/rates/countries\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-catalog/public/rates": {
      "get": {
        "operationId": "catalogRates",
        "tags": [
          "Numbers"
        ],
        "summary": "Outgoing call rates by prefix",
        "description": "Public paginated list of per-minute rates for outgoing calls by destination prefix (US cents per minute) with\nbilling increments. Filter by country (ISO-2 or exact name), route and search (digits — prefix starts with;\ntext — country or network name contains); sort by `country`, `prefix` or `price`. No token required; limit 120\nrequests per minute per IP; cached 5 minutes.\n\n\n**Required role:** no token required (public)\n\n**Side effects:** None",
        "security": [],
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "ISO-2 country code or exact country name.",
            "schema": {
              "type": "string",
              "example": "US"
            }
          },
          {
            "name": "route",
            "in": "query",
            "required": false,
            "description": "Route type (default `international`).",
            "schema": {
              "type": "string",
              "enum": [
                "international",
                "local",
                "all"
              ],
              "example": "international"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Digits — prefix starts with; text — country/network contains.",
            "schema": {
              "type": "string",
              "example": "1212"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort field (default `country`).",
            "schema": {
              "type": "string",
              "enum": [
                "country",
                "prefix",
                "price"
              ],
              "example": "price"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "description": "Sort direction (default `asc`).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "example": "asc"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, from 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Page size (1–200, default 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "example": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of rates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "description": "Currency (always `USD`).",
                      "example": "USD"
                    },
                    "unit": {
                      "type": "string",
                      "description": "Price unit (always `cent_per_minute`).",
                      "example": "cent_per_minute"
                    },
                    "route": {
                      "type": "string",
                      "description": "Applied route filter.",
                      "example": "international"
                    },
                    "updatedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Last rates import.",
                      "example": "2026-09-30T17:12:39.461Z"
                    },
                    "meta": {
                      "type": "object",
                      "description": "Pagination.",
                      "properties": {
                        "page": {
                          "type": "integer",
                          "description": "Current page.",
                          "example": 1
                        },
                        "pageSize": {
                          "type": "integer",
                          "description": "Page size.",
                          "example": 50
                        },
                        "total": {
                          "type": "integer",
                          "description": "Total rows.",
                          "example": 112477
                        }
                      }
                    },
                    "data": {
                      "type": "array",
                      "description": "Rates.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersCatalogRate"
                      }
                    }
                  }
                },
                "example": {
                  "currency": "USD",
                  "unit": "cent_per_minute",
                  "route": "international",
                  "updatedAt": "2026-09-30T17:12:39.461Z",
                  "meta": {
                    "page": 1,
                    "pageSize": 50,
                    "total": 112477
                  },
                  "data": [
                    {
                      "prefix": "1212",
                      "country": "United States",
                      "iso": "US",
                      "network": "US Fixed",
                      "route": "international",
                      "price": 2,
                      "increments": {
                        "initial": 1,
                        "recurring": 1
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-catalog/public/rates\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-catalog/public/rates\", {\n  method: \"GET\",\n  headers: { },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-catalog/public/rates\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, []);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-identities": {
      "get": {
        "operationId": "listIdentities",
        "tags": [
          "Numbers"
        ],
        "summary": "List identities of my company",
        "description": "Identities (a person or a business) are the owner records that regulators require before an international\nnumber from a regulated country can be activated. They are stored at the number provider and mirrored here.\nReturns all identities of the current user's company with their addresses, newest first (plain array, no\nStrapi envelope). Reuse a verified identity/address for new verification requests.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Identities with addresses.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NumbersIdentity"
                  }
                },
                "example": [
                  {
                    "id": 5,
                    "identity_type": "business",
                    "first_name": "John",
                    "last_name": "Doe",
                    "phone_number": "+380441234567",
                    "company_name": "Example LLC",
                    "company_reg_number": "12345678",
                    "vat_id": null,
                    "birth_date": null,
                    "id_number": null,
                    "personal_tax_id": null,
                    "contact_email": "owner@example.com",
                    "country_iso": "UA",
                    "birth_country_iso": null,
                    "verified": true,
                    "proofs": [
                      {
                        "didww_proof_id": "9a1b2c3d-0000-4000-8000-000000000001",
                        "proof_type_id": "7f0e1d2c-0000-4000-8000-000000000002",
                        "files": [
                          {
                            "name": "passport.pdf"
                          }
                        ],
                        "expires_at": "2027-01-15T00:00:00.000Z",
                        "created_at": "2026-01-15T10:00:00.000Z"
                      }
                    ],
                    "permanent_documents": [],
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "addresses": [
                      {
                        "id": 8,
                        "country_iso": "UA",
                        "city_name": "Kyiv",
                        "postal_code": "01001",
                        "address": "1 Example St",
                        "description": null,
                        "verified": true,
                        "proofs": [],
                        "createdAt": "2026-01-15T10:05:00.000Z"
                      }
                    ]
                  }
                ]
              }
            }
          },
          "400": {
            "description": "The user has no company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "User has no company",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-identities\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createIdentity",
        "tags": [
          "Numbers"
        ],
        "summary": "Create an identity",
        "description": "Creates an owner identity for number verification. The record is first created at the number provider and\nthen mirrored locally (if the provider rejects it, nothing is saved). Required: `identity_type`, `first_name`,\n`last_name`, `phone_number`; for `business` also `company_name`. Which other fields are mandatory for a given\ncountry/number type is returned by `GET /didww-verifications/requirements` (`mandatory_fields`).\nAfter creation add an address (`POST /didww-identities/{id}/addresses`) and upload proofs.\nLimit: 120 modifying requests per 5 minutes per IP across all identity/verification endpoints.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Creates the identity at the number provider (DIDWW). No charge.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/NumbersIdentityInput"
                  }
                }
              },
              "example": {
                "data": {
                  "identity_type": "business",
                  "first_name": "John",
                  "last_name": "Doe",
                  "phone_number": "+380441234567",
                  "company_name": "Example LLC",
                  "company_reg_number": "12345678",
                  "contact_email": "owner@example.com",
                  "country_iso": "UA"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created identity (plain object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersIdentity"
                },
                "example": {
                  "id": 5,
                  "identity_type": "business",
                  "first_name": "John",
                  "last_name": "Doe",
                  "phone_number": "+380441234567",
                  "company_name": "Example LLC",
                  "company_reg_number": "12345678",
                  "vat_id": null,
                  "birth_date": null,
                  "id_number": null,
                  "personal_tax_id": null,
                  "contact_email": "owner@example.com",
                  "country_iso": "UA",
                  "birth_country_iso": null,
                  "verified": false,
                  "proofs": [],
                  "permanent_documents": [],
                  "createdAt": "2026-01-15T10:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Messages: `identity_type must be personal or business`, `company_name is required for business identity`, `first_name is required`, `last_name is required`, `phone_number is required`, `User has no company`; or an error text from the provider.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "company_name is required for business identity",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/didww-identities\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"identity_type\": \"business\",\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe\",\n    \"phone_number\": \"+380441234567\",\n    \"company_name\": \"Example LLC\",\n    \"company_reg_number\": \"12345678\",\n    \"contact_email\": \"owner@example.com\",\n    \"country_iso\": \"UA\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"identity_type\": \"business\",\n      \"first_name\": \"John\",\n      \"last_name\": \"Doe\",\n      \"phone_number\": \"+380441234567\",\n      \"company_name\": \"Example LLC\",\n      \"company_reg_number\": \"12345678\",\n      \"contact_email\": \"owner@example.com\",\n      \"country_iso\": \"UA\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"identity_type\":\"business\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"phone_number\":\"+380441234567\",\"company_name\":\"Example LLC\",\"company_reg_number\":\"12345678\",\"contact_email\":\"owner@example.com\",\"country_iso\":\"UA\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-identities/{id}": {
      "put": {
        "operationId": "updateIdentity",
        "tags": [
          "Numbers"
        ],
        "summary": "Update an identity",
        "description": "Updates the identity at the provider and locally. Only these fields can be changed: `first_name`, `last_name`,\n`phone_number`, `company_name`, `company_reg_number`, `vat_id`, `birth_date`, `id_number`, `personal_tax_id`,\n`contact_email`. `identity_type`, `country_iso`, `birth_country_iso` cannot be changed — create a new identity.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Updates the identity at the number provider (DIDWW).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identity id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/NumbersIdentityUpdate"
                  }
                }
              },
              "example": {
                "data": {
                  "phone_number": "+380441234568",
                  "contact_email": "owner@example.com"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated identity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersIdentity"
                },
                "example": {
                  "id": 5,
                  "identity_type": "business",
                  "first_name": "John",
                  "last_name": "Doe",
                  "phone_number": "+380441234568",
                  "company_name": "Example LLC",
                  "company_reg_number": "12345678",
                  "vat_id": null,
                  "birth_date": null,
                  "id_number": null,
                  "personal_tax_id": null,
                  "contact_email": "owner@example.com",
                  "country_iso": "UA",
                  "birth_country_iso": null,
                  "verified": false,
                  "proofs": [],
                  "permanent_documents": [],
                  "createdAt": "2026-01-15T10:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Identity not found in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Identity not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/didww-identities/5\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"phone_number\": \"+380441234568\",\n    \"contact_email\": \"owner@example.com\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities/5\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"phone_number\": \"+380441234568\",\n      \"contact_email\": \"owner@example.com\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"phone_number\":\"+380441234568\",\"contact_email\":\"owner@example.com\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteIdentity",
        "tags": [
          "Numbers"
        ],
        "summary": "Delete an identity",
        "description": "Deletes the identity and all its addresses at the provider and locally. Not allowed while a verification\nrequest with this identity is `pending`.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Permanently deletes the identity and its addresses at the number provider and locally.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identity id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersOk"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "`Identity is used in a pending verification. Wait for its result first.`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Identity is used in a pending verification. Wait for its result first.",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Identity not found in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Identity not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/didww-identities/5\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities/5\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-identities/{id}/addresses": {
      "post": {
        "operationId": "createIdentityAddress",
        "tags": [
          "Numbers"
        ],
        "summary": "Add an address to an identity",
        "description": "Creates an address (registered/service address) for the identity at the provider and locally. Required:\n`country_iso`, `city_name`, `postal_code`, `address`. The address is then used in verification requests\n(`address_id`) and may need its own proofs (`POST /didww-identities/addresses/{id}/proofs`).\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Creates the address at the number provider (DIDWW).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identity id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "country_iso",
                      "city_name",
                      "postal_code",
                      "address"
                    ],
                    "description": "Address fields.",
                    "properties": {
                      "country_iso": {
                        "type": "string",
                        "description": "ISO-2 country code (stored upper-case).",
                        "example": "UA"
                      },
                      "city_name": {
                        "type": "string",
                        "description": "City.",
                        "example": "Kyiv"
                      },
                      "postal_code": {
                        "type": "string",
                        "description": "Postal code.",
                        "example": "01001"
                      },
                      "address": {
                        "type": "string",
                        "description": "Street address.",
                        "example": "1 Example St"
                      },
                      "description": {
                        "type": "string",
                        "description": "Optional note.",
                        "example": "Head office"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "country_iso": "UA",
                  "city_name": "Kyiv",
                  "postal_code": "01001",
                  "address": "1 Example St",
                  "description": "Head office"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersAddress"
                },
                "example": {
                  "id": 8,
                  "country_iso": "UA",
                  "city_name": "Kyiv",
                  "postal_code": "01001",
                  "address": "1 Example St",
                  "description": "Head office",
                  "verified": false,
                  "proofs": [],
                  "createdAt": "2026-01-15T10:05:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `country_iso is required`, `city_name is required`, `postal_code is required`, `address is required`, or a provider error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "postal_code is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Identity not found in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Identity not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/didww-identities/5/addresses\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"country_iso\": \"UA\",\n    \"city_name\": \"Kyiv\",\n    \"postal_code\": \"01001\",\n    \"address\": \"1 Example St\",\n    \"description\": \"Head office\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities/5/addresses\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"country_iso\": \"UA\",\n      \"city_name\": \"Kyiv\",\n      \"postal_code\": \"01001\",\n      \"address\": \"1 Example St\",\n      \"description\": \"Head office\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities/5/addresses\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"country_iso\":\"UA\",\"city_name\":\"Kyiv\",\"postal_code\":\"01001\",\"address\":\"1 Example St\",\"description\":\"Head office\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-identities/addresses/{id}": {
      "delete": {
        "operationId": "deleteIdentityAddress",
        "tags": [
          "Numbers"
        ],
        "summary": "Delete an address",
        "description": "Deletes the address at the provider and locally. Not allowed while a `pending` verification uses it.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Permanently deletes the address at the number provider and locally.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Address id.",
            "schema": {
              "type": "integer",
              "example": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersOk"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "`Address is used in a pending verification. Wait for its result first.`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Address is used in a pending verification. Wait for its result first.",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Address not found in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Address not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/didww-identities/addresses/8\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities/addresses/8\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities/addresses/8\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-identities/{id}/proofs": {
      "post": {
        "operationId": "uploadIdentityProofs",
        "tags": [
          "Numbers"
        ],
        "summary": "Upload identity proof documents",
        "description": "`multipart/form-data`: one or more files in the `files` field plus `proof_type_id` — the id of a proof type from\n`GET /didww-verifications/requirements` (`personal.proof_types` / `business.proof_types`). Files are encrypted and\nsent to the provider; the platform keeps only a journal entry (file names, proof type, expiry) in `proofs`.\nAllowed: JPG, PNG, PDF, max 10 MB each.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Uploads the documents to the number provider (DIDWW) and attaches them to the identity. Files are not stored on the platform.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identity id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/NumbersProofUpload"
              },
              "example": {
                "proof_type_id": "7f0e1d2c-0000-4000-8000-000000000002",
                "files": "(binary passport.pdf)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated proofs journal.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "proofs": {
                      "type": "array",
                      "description": "All proofs of the identity.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersProofEntry"
                      }
                    }
                  }
                },
                "example": {
                  "proofs": [
                    {
                      "didww_proof_id": "9a1b2c3d-0000-4000-8000-000000000001",
                      "proof_type_id": "7f0e1d2c-0000-4000-8000-000000000002",
                      "files": [
                        {
                          "name": "passport.pdf"
                        }
                      ],
                      "expires_at": "2027-01-15T00:00:00.000Z",
                      "created_at": "2026-01-15T10:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `proof_type_id is required`, `No files provided`, `File \"x\": only JPG, PNG or PDF allowed`, `File \"x\": max size is 10MB`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "No files provided",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Identity not found in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Identity not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/didww-identities/5/proofs\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities/5/proofs\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities/5/proofs\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-identities/addresses/{id}/proofs": {
      "post": {
        "operationId": "uploadAddressProofs",
        "tags": [
          "Numbers"
        ],
        "summary": "Upload address proof documents",
        "description": "Same as identity proofs, but for an address (`address.proof_types` in the requirements). `multipart/form-data` with `files` and `proof_type_id`.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Uploads the documents to the number provider (DIDWW) and attaches them to the address.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Address id.",
            "schema": {
              "type": "integer",
              "example": 8
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/NumbersProofUpload"
              },
              "example": {
                "proof_type_id": "7f0e1d2c-0000-4000-8000-000000000003",
                "files": "(binary utility-bill.pdf)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated proofs journal of the address.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "proofs": {
                      "type": "array",
                      "description": "All proofs of the address.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersProofEntry"
                      }
                    }
                  }
                },
                "example": {
                  "proofs": [
                    {
                      "didww_proof_id": "9a1b2c3d-0000-4000-8000-000000000004",
                      "proof_type_id": "7f0e1d2c-0000-4000-8000-000000000003",
                      "files": [
                        {
                          "name": "utility-bill.pdf"
                        }
                      ],
                      "expires_at": null,
                      "created_at": "2026-01-15T10:10:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `proof_type_id is required`, `No files provided`, file type/size errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "proof_type_id is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Address not found in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Address not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/didww-identities/addresses/8/proofs\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities/addresses/8/proofs\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities/addresses/8/proofs\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-identities/{id}/supporting-documents": {
      "post": {
        "operationId": "uploadIdentitySupportingDocuments",
        "tags": [
          "Numbers"
        ],
        "summary": "Upload a filled supporting document (LOA / registration form)",
        "description": "`multipart/form-data`: `files` plus `template_id` — id of the permanent document template from the requirements\n(`personal.permanent_document` / `business.permanent_document`, the template file is at its `url`). The filled\nand signed form is encrypted, sent to the provider and attached to the identity permanently.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Uploads the document to the number provider (DIDWW) and attaches it to the identity.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identity id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "files",
                  "template_id"
                ],
                "properties": {
                  "files": {
                    "type": "string",
                    "format": "binary",
                    "description": "One or more files (JPG, PNG, PDF, max 10 MB each)."
                  },
                  "template_id": {
                    "type": "string",
                    "description": "Template id from the requirements.",
                    "example": "5e4d3c2b-0000-4000-8000-000000000005"
                  }
                }
              },
              "example": {
                "template_id": "5e4d3c2b-0000-4000-8000-000000000005",
                "files": "(binary loa-signed.pdf)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated journal of permanent documents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "permanent_documents": {
                      "type": "array",
                      "description": "All permanent documents of the identity.",
                      "items": {
                        "$ref": "#/components/schemas/NumbersSupportingDocumentEntry"
                      }
                    }
                  }
                },
                "example": {
                  "permanent_documents": [
                    {
                      "didww_document_id": "3c2b1a09-0000-4000-8000-000000000006",
                      "template_id": "5e4d3c2b-0000-4000-8000-000000000005",
                      "files": [
                        {
                          "name": "loa-signed.pdf"
                        }
                      ],
                      "created_at": "2026-01-15T10:20:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `template_id is required`, `No files provided`, file type/size errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "template_id is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Identity not found in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Identity not found",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/didww-identities/5/supporting-documents\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-identities/5/supporting-documents\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-identities/5/supporting-documents\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-verifications/requirements": {
      "get": {
        "operationId": "getVerificationRequirements",
        "tags": [
          "Numbers"
        ],
        "summary": "Verification requirements for a prefix or a number",
        "description": "Tells whether owner verification is required and which identity fields and documents the regulator demands.\nPass `prefix_id` (a `phone-prefix` id — before buying) or `phone_id` (a number of your company — after buying;\ntakes precedence). `required: false` means the number can be used without documents. When `required: true`,\n`requirement` describes, separately for `personal` and `business` identities: mandatory identity fields,\nhow many proofs are needed (`proof_qty`), accepted proof types (use their `id` as `proof_type_id` when uploading),\nthe permanent document template (`template_id`), the one-time document for the request itself, and the address\nproof rules. `identity_type` limits which identity kind is accepted (`personal`, `business` or `any`).\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None (reads the requirement from the number provider).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "prefix_id",
            "in": "query",
            "required": false,
            "description": "Prefix id (`GET /phone-prefixes`).",
            "schema": {
              "type": "integer",
              "example": 9
            }
          },
          {
            "name": "phone_id",
            "in": "query",
            "required": false,
            "description": "Id of a number of your company.",
            "schema": {
              "type": "integer",
              "example": 35
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Requirement.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "required": {
                      "type": "boolean",
                      "description": "Whether verification is required.",
                      "example": true
                    },
                    "requirement": {
                      "description": "Requirement details, or null when not required.",
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/NumbersRequirement"
                        },
                        {
                          "type": "null",
                          "description": "Null when not required."
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "required": true,
                  "requirement": {
                    "id": "a1b2c3d4-0000-4000-8000-000000000007",
                    "identity_type": "any",
                    "service_description_required": false,
                    "restriction_message": null,
                    "personal": {
                      "area_level": "country",
                      "proof_qty": 1,
                      "mandatory_fields": [
                        "first_name",
                        "last_name",
                        "birth_date",
                        "id_number"
                      ],
                      "proof_types": [
                        {
                          "id": "7f0e1d2c-0000-4000-8000-000000000002",
                          "name": "Passport"
                        }
                      ],
                      "permanent_document": null,
                      "onetime_document": null
                    },
                    "business": {
                      "area_level": "country",
                      "proof_qty": 1,
                      "mandatory_fields": [
                        "company_name",
                        "company_reg_number"
                      ],
                      "proof_types": [
                        {
                          "id": "7f0e1d2c-0000-4000-8000-000000000008",
                          "name": "Company registration certificate"
                        }
                      ],
                      "permanent_document": {
                        "id": "5e4d3c2b-0000-4000-8000-000000000005",
                        "name": "Letter of authorization",
                        "url": "https://example.com/templates/loa.pdf"
                      },
                      "onetime_document": null
                    },
                    "address": {
                      "area_level": "country",
                      "proof_qty": 1,
                      "proof_types": [
                        {
                          "id": "7f0e1d2c-0000-4000-8000-000000000003",
                          "name": "Utility bill"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`prefix_id or phone_id is required`, `Phone prefix not found`, `User has no company`, or a provider error (e.g. `DIDWW: cannot resolve verification requirements for 380441234567`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "prefix_id or phone_id is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "`Phone not found` — the number is not in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Phone not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-verifications/requirements\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-verifications/requirements\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-verifications/requirements\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-verifications/pending-phones": {
      "get": {
        "operationId": "listPendingVerificationPhones",
        "tags": [
          "Numbers"
        ],
        "summary": "Numbers awaiting verification",
        "description": "Numbers of your company with `verificationRequired: true` and no `verifiedAt` yet, together with the status\nof the latest verification request for each (`none` — no request yet, `pending`, `rejected` with reasons).\nSuch numbers are bought but not yet active for calls.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Numbers awaiting verification (plain array).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NumbersPendingPhone"
                  }
                },
                "example": [
                  {
                    "phone_id": 41,
                    "fullnumber": "15551234567",
                    "verification_status": "rejected",
                    "verification_id": 3,
                    "reject_reasons": [
                      "Document is not readable"
                    ],
                    "reject_comment": "Please upload a colour scan"
                  },
                  {
                    "phone_id": 42,
                    "fullnumber": "15551234568",
                    "verification_status": "none",
                    "verification_id": null,
                    "reject_reasons": null,
                    "reject_comment": null
                  }
                ]
              }
            }
          },
          "400": {
            "description": "The user has no company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "User has no company",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-verifications/pending-phones\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-verifications/pending-phones\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-verifications/pending-phones\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-verifications": {
      "get": {
        "operationId": "listVerifications",
        "tags": [
          "Numbers"
        ],
        "summary": "List verification requests",
        "description": "Verification requests of your company, newest first, with numbers, identity and address (plain array). Optional filters `status` and `phone_id`.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "rejected"
              ],
              "example": "pending"
            }
          },
          {
            "name": "phone_id",
            "in": "query",
            "required": false,
            "description": "Only requests that include this number.",
            "schema": {
              "type": "integer",
              "example": 41
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Requests.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NumbersVerification"
                  }
                },
                "example": [
                  {
                    "id": 3,
                    "status": "pending",
                    "reference": "AV-000123",
                    "service_description": "Customer support line",
                    "reject_reasons": null,
                    "reject_comment": null,
                    "createdAt": "2026-01-16T09:00:00.000Z",
                    "phones": [
                      {
                        "id": 41,
                        "fullnumber": "15551234567"
                      }
                    ],
                    "identity": {
                      "id": 5,
                      "identity_type": "business",
                      "first_name": "John",
                      "last_name": "Doe",
                      "company_name": "Example LLC"
                    },
                    "address": {
                      "id": 8,
                      "country_iso": "US",
                      "city_name": "New York",
                      "address": "1 Example St"
                    }
                  }
                ]
              }
            }
          },
          "400": {
            "description": "The user has no company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "User has no company",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-verifications\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-verifications\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-verifications\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createVerification",
        "tags": [
          "Numbers"
        ],
        "summary": "Submit a verification request",
        "description": "Submits bought numbers that require verification (`verificationRequired: true`, `verifiedAt: null`) to the\nprovider's compliance team together with an identity and one of its addresses. Accepts JSON\n`{ \"data\": { ... } }` or `multipart/form-data` with the same JSON as a string in the `data` field plus optional\none-time documents in `files` (when the requirement lists `onetime_document`). All numbers must belong to your\ncompany; a number may have only one `pending` request at a time. The result arrives asynchronously: the status\nbecomes `approved` (numbers get `verifiedAt` and are activated) or `rejected` (`reject_reasons`,\n`reject_comment`; submit a new request). Poll `GET /didww-verifications/{id}` or `pending-phones`; the company\nalso gets an in-app notification. Limit: 120 modifying requests per 5 minutes per IP.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Creates an address verification request at the number provider (DIDWW) and uploads the one-time documents. No charge. On approval the numbers are activated and a notification is sent.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/NumbersVerificationInput"
                  }
                }
              },
              "example": {
                "data": {
                  "phone_ids": [
                    41
                  ],
                  "identity_id": 5,
                  "address_id": 8,
                  "service_description": "Customer support line"
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "string",
                    "description": "JSON string with the same fields as the JSON body `data`.",
                    "example": "{\"phone_ids\":[41],\"identity_id\":5,\"address_id\":8}"
                  },
                  "files": {
                    "type": "string",
                    "format": "binary",
                    "description": "Optional one-time documents (JPG, PNG, PDF, max 10 MB each)."
                  }
                }
              },
              "example": {
                "data": "{\"phone_ids\":[41],\"identity_id\":5,\"address_id\":8}",
                "files": "(binary onetime-form.pdf)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created request (plain object).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "description": "Request id.",
                      "example": 3
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "approved",
                        "rejected"
                      ],
                      "description": "Status.",
                      "example": "pending"
                    },
                    "reference": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Provider reference.",
                      "example": "AV-000123"
                    },
                    "service_description": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Service description.",
                      "example": "Customer support line"
                    },
                    "reject_reasons": {
                      "type": [
                        "array",
                        "null"
                      ],
                      "items": {
                        "type": "string"
                      },
                      "description": "Rejection reasons.",
                      "example": null
                    },
                    "reject_comment": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Rejection comment.",
                      "example": null
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Created at.",
                      "example": "2026-01-16T09:00:00.000Z"
                    }
                  }
                },
                "example": {
                  "id": 3,
                  "status": "pending",
                  "reference": "AV-000123",
                  "service_description": "Customer support line",
                  "reject_reasons": null,
                  "reject_comment": null,
                  "createdAt": "2026-01-16T09:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `phone_ids is required`, `identity_id and address_id are required`, `Address does not belong to the selected identity`, `Phones do not need verification: 380441234567`, `Some of these phones already have a pending verification`, `Invalid JSON in data field`, `No files provided`, file type/size errors, `DIDWW DID not found for number ...`, or a provider error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Some of these phones already have a pending verification",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "`Identity not found`, `Address not found`, `Some phones not found in your company`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Some phones not found in your company",
                    "details": {}
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/didww-verifications\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"phone_ids\": [\n      41\n    ],\n    \"identity_id\": 5,\n    \"address_id\": 8,\n    \"service_description\": \"Customer support line\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-verifications\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"phone_ids\": [\n        41\n      ],\n      \"identity_id\": 5,\n      \"address_id\": 8,\n      \"service_description\": \"Customer support line\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-verifications\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"phone_ids\":[41],\"identity_id\":5,\"address_id\":8,\"service_description\":\"Customer support line\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/didww-verifications/{id}": {
      "get": {
        "operationId": "getVerification",
        "tags": [
          "Numbers"
        ],
        "summary": "Get a verification request",
        "description": "One verification request of your company with numbers, identity and address.\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Request id.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumbersVerification"
                },
                "example": {
                  "id": 3,
                  "status": "rejected",
                  "reference": "AV-000123",
                  "service_description": "Customer support line",
                  "reject_reasons": [
                    "Document is not readable"
                  ],
                  "reject_comment": "Please upload a colour scan",
                  "createdAt": "2026-01-16T09:00:00.000Z",
                  "phones": [
                    {
                      "id": 41,
                      "fullnumber": "15551234567"
                    }
                  ],
                  "identity": {
                    "id": 5,
                    "identity_type": "business",
                    "first_name": "John",
                    "last_name": "Doe",
                    "company_name": "Example LLC"
                  },
                  "address": {
                    "id": 8,
                    "country_iso": "US",
                    "city_name": "New York",
                    "address": "1 Example St"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The user has no company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "User has no company",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "`Verification not found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Verification not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/didww-verifications/3\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/didww-verifications/3\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/didww-verifications/3\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/add-external-number": {
      "post": {
        "operationId": "addExternalNumber",
        "tags": [
          "Numbers"
        ],
        "summary": "Add an external (own) number via SIP trunk",
        "description": "An external number (BYON) is a number you already own at another operator and connect to the platform through a\nSIP trunk (`sip_trunk` — id of your trunk, see the SIP group). This call only registers the number in a\n\"pending verification\" state (`verifiedAt: null`, `reserved: true`, class `exclusive`, 100 channels). Nothing is\ncharged yet. Next steps: `POST /sip-trunks/external-numbers/{phone_id}/request-verification` (SMS with a code\nto the number) and `.../confirm-verification` (activates the number and starts the monthly subscription).\n\n`phone` accepts any formatting (`+38 (044) 123-45-67`); it is normalised to E.164 digits without plus and\nstored in `fullnumber`. The number must be a valid phone number and must not exist on the platform yet.\nBody is a flat object (no `data` envelope).\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Creates the number record in your company (not active). If the trunk uses login/password credentials, a registration for the number is created in the telephony core.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "sip_trunk"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "Your number in any format.",
                    "example": "+380441234567"
                  },
                  "sip_trunk": {
                    "type": "integer",
                    "description": "Id of your SIP trunk.",
                    "example": 3
                  }
                }
              },
              "example": {
                "phone": "+380441234567",
                "sip_trunk": 3
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created external number.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NumbersExternalPhoneShort"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 77,
                    "attributes": {
                      "fullnumber": "380441234567",
                      "verifiedAt": null
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `phone and sip_trunk are required fields`, `phone is required`, `Invalid phone number: <input>`, `Phone already exist`, `Active company is required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Phone already exist",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "`SIP trunk not found` — the trunk is not in your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "SIP trunk not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/sip-trunks/add-external-number\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"phone\": \"+380441234567\",\n  \"sip_trunk\": 3\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/add-external-number\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"phone\": \"+380441234567\",\n    \"sip_trunk\": 3\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/add-external-number\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"phone\":\"+380441234567\",\"sip_trunk\":3}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/update-external-number/{id}/{phone_id}": {
      "put": {
        "operationId": "updateExternalNumber",
        "tags": [
          "Numbers"
        ],
        "summary": "Change an external number",
        "description": "Changes the digits and/or the channel limit of an external number attached to trunk `id`. `phone` is required\n(send the current value if only `channels` changes); `channels` defaults to 100 when omitted. Changing the digits\ndoes not reset verification. Body is a flat object.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Re-provisions routing and trunk registration for the number in the telephony core.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "SIP trunk id.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          },
          {
            "name": "phone_id",
            "in": "path",
            "required": true,
            "description": "External number id.",
            "schema": {
              "type": "integer",
              "example": 77
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "Number in any format.",
                    "example": "+380441234567"
                  },
                  "channels": {
                    "type": "integer",
                    "description": "Simultaneous calls limit (default 100).",
                    "example": 10
                  }
                }
              },
              "example": {
                "phone": "+380441234567",
                "channels": 10
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated number.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Number.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Number id.",
                          "example": 77
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Attributes.",
                          "properties": {
                            "fullnumber": {
                              "type": "string",
                              "description": "E.164 digits without plus.",
                              "example": "380441234567"
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 77,
                    "attributes": {
                      "fullnumber": "380441234567"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`phone are required fields`, `Invalid phone number: <input>`, `Phone already exist`, `Active company is required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "phone are required fields",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "`SIP trunk not found` or `External number not found` (number is not on this trunk).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "External number not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/sip-trunks/update-external-number/3/77\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"phone\": \"+380441234567\",\n  \"channels\": 10\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/update-external-number/3/77\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"phone\": \"+380441234567\",\n    \"channels\": 10\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/update-external-number/3/77\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"phone\":\"+380441234567\",\"channels\":10}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/delete-external-number/{id}/{phone_id}": {
      "delete": {
        "operationId": "deleteExternalNumber",
        "tags": [
          "Numbers"
        ],
        "summary": "Delete an external number",
        "description": "Removes the external number from trunk `id`: cancels (deletes) its monthly subscription and deletes the number\nrecord. No refund for the current period. If the number is not found on the trunk the response is `data: null`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Permanently deletes the number record and its subscription; unregisters the number in the telephony core.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "SIP trunk id.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          },
          {
            "name": "phone_id",
            "in": "path",
            "required": true,
            "description": "External number id.",
            "schema": {
              "type": "integer",
              "example": 77
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted number (or `data: null` if not found).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Deleted number.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Number id.",
                          "example": 77
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Attributes.",
                          "properties": {
                            "fullnumber": {
                              "type": "string",
                              "description": "E.164 digits without plus.",
                              "example": "380441234567"
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 77,
                    "attributes": {
                      "fullnumber": "380441234567"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Active company is required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Active company is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "`SIP trunk not found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "SIP trunk not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/sip-trunks/delete-external-number/3/77\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/delete-external-number/3/77\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/delete-external-number/3/77\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/external-numbers/{phone_id}/request-verification": {
      "post": {
        "operationId": "requestExternalNumberVerification",
        "tags": [
          "Numbers"
        ],
        "summary": "Send a verification code to an external number",
        "description": "Proves that you own the external number: generates a 4-digit code and sends it by SMS to the number\n(sender `OnePhone`; text `OnePhone verification code: 1234. Expires in 5 min.`). The code is valid for\n5 minutes (`expiresIn`), can be re-requested after 30 seconds (`resendIn`), at most 3 SMS per hour per number,\nand allows 3 wrong attempts (`attemptsAllowed`). Each SMS costs 0.20 (in the company's balance currency) and is\ncharged from the balance; the balance must cover it. Only for not-yet-verified numbers of your company.\nNo request body.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved)\n\n**Side effects:** Sends an SMS with a one-time code to the number and charges 0.20 (company currency) from the balance (a payment record is created); the SMS is stored in the company's SMS history.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "phone_id",
            "in": "path",
            "required": true,
            "description": "External number id.",
            "schema": {
              "type": "integer",
              "example": 77
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Code sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Result.",
                      "properties": {
                        "attributes": {
                          "type": "object",
                          "description": "Code parameters.",
                          "properties": {
                            "expiresIn": {
                              "type": "integer",
                              "description": "Code lifetime, seconds.",
                              "example": 300
                            },
                            "resendIn": {
                              "type": "integer",
                              "description": "Seconds before a new code may be requested.",
                              "example": 30
                            },
                            "attemptsAllowed": {
                              "type": "integer",
                              "description": "Allowed wrong attempts.",
                              "example": 3
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "attributes": {
                      "expiresIn": 300,
                      "resendIn": 30,
                      "attemptsAllowed": 3
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Phone already verified`, `Active company is required`, `Not enough money on balance`, `Wait 25s before resending`, `Too many SMS sent. Try again in 42 min`, `Verification request failed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ApplicationError",
                    "message": "Not enough money on balance",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "`External number not found` — not an external number of your company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "External number not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/request-verification\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/request-verification\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/request-verification\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/external-numbers/{phone_id}/confirm-verification": {
      "post": {
        "operationId": "confirmExternalNumberVerification",
        "tags": [
          "Numbers"
        ],
        "summary": "Confirm the code and activate an external number",
        "description": "Checks the SMS code. On success the number is activated: the balance is checked for the monthly fee of 8\n(company's balance currency), a monthly subscription for the external number is created (`active` =\n`auto_renewal`, default `true`), the first month is charged immediately, and the number gets `verifiedAt` and\n`reserved: false`. Wrong code decrements the attempts; after 3 wrong attempts the code is dropped and a new one\nmust be requested. Body may be flat or wrapped in `data`.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved)\n\n**Side effects:** Charges 8 (company balance currency) from the balance for the first month, creates a monthly subscription (auto-renew on unless `auto_renewal: false`), activates the number.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "phone_id",
            "in": "path",
            "required": true,
            "description": "External number id.",
            "schema": {
              "type": "integer",
              "example": 77
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "4-digit code from the SMS (leading zeros matter).",
                    "example": "0427"
                  },
                  "auto_renewal": {
                    "type": "boolean",
                    "description": "Enable monthly auto-renewal (default true).",
                    "example": true
                  }
                }
              },
              "example": {
                "code": "0427",
                "auto_renewal": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Number activated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NumbersExternalPhoneShort"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 77,
                    "attributes": {
                      "fullnumber": "380441234567",
                      "verifiedAt": "2026-01-15T10:30:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`code is required`, `Phone already verified`, `Active company is required`, `No active verification code. Request a new one.`, `Wrong code. 2 attempts left.`, `Too many wrong attempts. Request a new code.`, `Not enough money on balance`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Wrong code. 2 attempts left.",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "`External number not found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "External number not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/confirm-verification\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"code\": \"0427\",\n  \"auto_renewal\": true\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/confirm-verification\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"code\": \"0427\",\n    \"auto_renewal\": true\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/confirm-verification\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"code\":\"0427\",\"auto_renewal\":true}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/external-numbers/{phone_id}/auto-renewal": {
      "put": {
        "operationId": "toggleExternalNumberAutoRenewal",
        "tags": [
          "Numbers"
        ],
        "summary": "Toggle auto-renewal of an external number",
        "description": "Enables or disables monthly auto-renewal of the external number's subscription (`active`). With auto-renewal\noff the number stops working when the paid period ends. Returns the subscription id and the new state.\nBody may be flat or wrapped in `data`.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved)\n\n**Side effects:** Changes the subscription's `active` flag and sends an `autorenew_enabled` / `autorenew_cancelled` notification to the owner (Telegram and webhook).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "phone_id",
            "in": "path",
            "required": true,
            "description": "External number id.",
            "schema": {
              "type": "integer",
              "example": 77
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "active"
                ],
                "properties": {
                  "active": {
                    "type": "boolean",
                    "description": "true — auto-renew, false — stop at period end.",
                    "example": false
                  }
                }
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Subscription.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Subscription id.",
                          "example": 910
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Attributes.",
                          "properties": {
                            "active": {
                              "type": "boolean",
                              "description": "Auto-renewal state.",
                              "example": false
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 910,
                    "attributes": {
                      "active": false
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`active (boolean) is required`, `Active company is required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "active (boolean) is required",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission, or the company is disabled (`Company disabled`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          },
          "404": {
            "description": "`External number not found` or `Subscription for this number not found` (number not yet activated).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Subscription for this number not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/auto-renewal\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"active\": false\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/auto-renewal\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"active\": false\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/external-numbers/77/auto-renewal\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"active\":false}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/orders": {
      "get": {
        "operationId": "listOrders",
        "tags": [
          "Orders"
        ],
        "summary": "Orders of the company",
        "description": "Strapi core list of the company's orders (the company filter is added automatically). `meta.totals.amount`\nis the sum of `amount` over the whole filtered set in the company currency.\nUseful filters: `filters[orderStatus][$eq]=approved`, `filters[orderType][$eq]=incoming` (top-ups) /\n`outgoing` (purchases), `filters[createdAt][$gte]=2026-01-01`, `sort=createdAt:desc`.\nUseful populate: `populate[payment_system][fields][0]=name`, `populate[currency][fields][0]=name_short`,\n`populate[phones][fields][0]=fullnumber`, `populate[payments]=true`.\n`orderJSON` is the full snapshot of what was ordered and at what price (see `OrdersOrderJSON`).\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of orders with totals.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Orders.",
                      "items": {
                        "$ref": "#/components/schemas/OrdersOrder"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "description": "Pagination and totals.",
                      "properties": {
                        "pagination": {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        "totals": {
                          "type": "object",
                          "description": "Sum of `amount` over the whole filtered set.",
                          "properties": {
                            "amount": {
                              "type": "number",
                              "description": "Total.",
                              "example": 942.69
                            },
                            "currency": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Company currency code.",
                              "example": "USD"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 42,
                      "attributes": {
                        "orderId": "1788231620358546",
                        "orderStatus": "approved",
                        "orderType": "outgoing",
                        "amount": 6.49,
                        "auto_renewal": true,
                        "redirect_link": "global",
                        "source": "default",
                        "template_name": null,
                        "additional_data": null,
                        "orderJSON": {
                          "total": 6.49,
                          "goodsTotal": 5.99,
                          "starterFee": 0.5,
                          "refill": 0.5,
                          "tariff": null,
                          "esim": null,
                          "promo": null,
                          "domain": [],
                          "recall_widget": [],
                          "callback_widget": [],
                          "sip-trunk": [],
                          "incomingOrderId": null,
                          "phones": [
                            {
                              "prefix_id": 9,
                              "prefix_number": "44",
                              "quantity": 1,
                              "setup_price": 5.99,
                              "attached_phones": [
                                {
                                  "id": 25,
                                  "service": "default",
                                  "fullnumber": "380441234567"
                                }
                              ]
                            }
                          ],
                          "payment": {
                            "url": "https://api.onevoiplanet.com/api/orders/process-payment/1788231620358546"
                          }
                        },
                        "createdAt": "2026-09-01T03:00:20.402Z",
                        "updatedAt": "2026-09-01T03:00:25.402Z",
                        "payment_system": {
                          "data": {
                            "id": 3,
                            "attributes": {
                              "name": "Stripe"
                            }
                          }
                        },
                        "currency": {
                          "data": {
                            "id": 1,
                            "attributes": {
                              "name_short": "USD"
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 2,
                      "total": 34
                    },
                    "totals": {
                      "amount": 942.69,
                      "currency": "USD"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/orders\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/orders\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/orders\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Create an order and start payment",
        "description": "Creates an order **and immediately starts the payment**. This is the only way to buy numbers, talk packets,\ntariffs, eSIM, extra domains/widgets/SIP trunks or to top up the balance.\n\n**Payment method** (`payment_system`):\n* `0` — pay from the company balance. The total is charged right away; the order becomes `approved`\n  (or `declined` with `orderJSON.payment.message = \"Not enough money on balance\"`) within a few seconds.\n* any other number (e.g. `1`) — pay by card. The gateway is chosen by the country of the client IP:\n  Ukraine → WayForPay, elsewhere → Stripe. The response contains `orderJSON.payment.url` (Stripe) or\n  `orderJSON.payment.invoiceUrl` (WayForPay): open it in a browser — it redirects to the gateway checkout page\n  and back to the cabinet. Mobile apps pass `paymentType: \"intent\"` and then call\n  `POST /payment-system/stripe/payment-sheet`. After the gateway confirms, the full total is credited to the\n  balance and the goods price (`total - refill`) is charged from it, so the balance grows only by `refill`.\n\n**What can be ordered** (any combination; all fields optional):\n* `phones` — new numbers: `{ prefix_id, quantity, talk_package_id? }`. Numbers from the own pool are\n  attached immediately; numbers of the DIDWW provider (`attached_phones[].service = \"didww\"`) are bought in\n  the background — money is charged at once, the number appears in `GET /phones` a bit later\n  (progress in `additional_data.didww_pending`). If the provider fails, the amount is refunded to the balance.\n* `phones` — talk packet / renewal for an owned number: `{ prefix_id, phone_id, talk_package_id, quantity: 1 }`.\n  Only the packet price is charged.\n* `tariff` — switch to a tariff (monthly fee is charged; the company must fit the tariff limits).\n* `esim` — eSIM bundle `{ id }` (or `{ id, name, refills: [...] }` for a top-up of an existing eSIM); eSIM is bought from the provider after payment.\n* `domains`, `recall_widgets`, `callback_widgets`, `attachServices` (SIP trunks) — paid extras above the tariff limits.\n* `addMoney` — balance top-up amount (minimum 5). A pure top-up is an `incoming` order; combined with goods the whole order is `outgoing`.\n* `promoCode` — promo code (currently applies to eSIM prices).\n* `auto_renewal` — enable monthly auto-renewal of the subscriptions created by this order (default `true`).\n\n**Starter fee**: when the order contains a number and the balance after purchase would be below the threshold\n(0.5 USD / 20 UAH), the threshold amount is added to `refill` so that the number can receive calls.\n\n**Lifecycle**: `new` → `approved` | `declined`. An unpaid card order expires (becomes `declined`) after 30 minutes\nand reserved numbers are released. On `approved` a subscription is created for every purchased number/tariff/extra\n(see Subscriptions) and the owner receives an email/push; on `declined` reserved items are released.\nUse `POST /orders/calculate-order-price` first to show the price without charging.\nBody is a Strapi envelope `{ \"data\": { ... } }`. Rate limit: 30 requests per 5 minutes on the payment redirect.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** Charges money: with `payment_system: 0` the order total (`orderJSON.total`) is charged from the company balance immediately; with a card gateway the total is charged to the card and the goods part is then debited from the balance (balance grows only by `refill`). Reserves numbers from the pool; buys numbers at DIDWW and eSIM at the provider after approval; creates subscriptions (monthly auto-charges) for every purchased item; sends email/push notifications to the owner; an unpaid order is auto-declined after 30 minutes.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/OrdersCreateInput"
                  }
                }
              },
              "examples": {
                "buyNumberFromBalance": {
                  "summary": "Buy a number from the balance",
                  "value": {
                    "data": {
                      "payment_system": 0,
                      "phones": [
                        {
                          "prefix_id": 9,
                          "quantity": 1
                        }
                      ],
                      "auto_renewal": true
                    }
                  }
                },
                "buyNumberWithPacketByCard": {
                  "summary": "Number + talk packet, pay by card",
                  "value": {
                    "data": {
                      "payment_system": 1,
                      "phones": [
                        {
                          "prefix_id": 9,
                          "quantity": 1,
                          "talk_package_id": 2
                        }
                      ],
                      "auto_renewal": true
                    }
                  }
                },
                "renewTalkPacket": {
                  "summary": "Talk packet for an owned number",
                  "value": {
                    "data": {
                      "payment_system": 0,
                      "phones": [
                        {
                          "prefix_id": 9,
                          "phone_id": 25,
                          "talk_package_id": 2,
                          "quantity": 1
                        }
                      ]
                    }
                  }
                },
                "topUp": {
                  "summary": "Top up the balance by card",
                  "value": {
                    "data": {
                      "payment_system": 1,
                      "addMoney": 20
                    }
                  }
                },
                "topUpMobile": {
                  "summary": "Top up from a mobile app (Stripe PaymentSheet)",
                  "value": {
                    "data": {
                      "payment_system": 1,
                      "addMoney": 20,
                      "paymentType": "intent"
                    }
                  }
                },
                "esim": {
                  "summary": "Buy an eSIM bundle with a promo code",
                  "value": {
                    "data": {
                      "payment_system": 0,
                      "esim": {
                        "id": 120
                      },
                      "promoCode": "WELCOME10"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created order (flat object, not a Strapi envelope). Status is `new`; poll `GET /orders/{id}` for `approved`/`declined`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrdersOrderFlat"
                },
                "example": {
                  "id": 43,
                  "orderId": "1790923456789123",
                  "orderStatus": "new",
                  "orderType": "outgoing",
                  "amount": 6.49,
                  "auto_renewal": true,
                  "redirect_link": "global",
                  "source": "default",
                  "template_name": null,
                  "additional_data": null,
                  "orderJSON": {
                    "total": 6.49,
                    "goodsTotal": 5.99,
                    "starterFee": 0.5,
                    "refill": 0.5,
                    "tariff": null,
                    "esim": null,
                    "promo": null,
                    "domain": [],
                    "recall_widget": [],
                    "callback_widget": [],
                    "sip-trunk": [],
                    "incomingOrderId": null,
                    "user_ip": "203.0.113.10",
                    "phones": [
                      {
                        "prefix_id": 9,
                        "prefix_number": "44",
                        "quantity": 1,
                        "setup_price": 5.99,
                        "attached_phones": [
                          {
                            "id": 25,
                            "service": "default",
                            "fullnumber": "380441234567"
                          }
                        ]
                      }
                    ],
                    "payment": {
                      "url": "https://api.onevoiplanet.com/api/orders/process-payment/1790923456789123"
                    }
                  },
                  "createdAt": "2026-10-02T10:00:00.000Z",
                  "updatedAt": "2026-10-02T10:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "paymentSystemRequired": {
                    "summary": "payment_system missing",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "paysystem || payment_system field required",
                        "details": {}
                      }
                    }
                  },
                  "paymentSystemNotFound": {
                    "summary": "Payment system not found",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Платіжна система не знайдена!",
                        "details": {}
                      }
                    }
                  },
                  "amountTooSmall": {
                    "summary": "Top-up below minimum",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Amount must be greater than 5",
                        "details": {}
                      }
                    }
                  },
                  "phonesInvalid": {
                    "summary": "phones array invalid",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "phones[0].prefix_id is a required field",
                        "details": {}
                      }
                    }
                  },
                  "renewInvalid": {
                    "summary": "Renewal without packet",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "phone_id requires talk_package_id and quantity=1",
                        "details": {}
                      }
                    }
                  },
                  "maxPhones": {
                    "summary": "Tariff number limit reached",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Неможливо придбати телефон: максимально допустиме значення телефонів вже придбано",
                        "details": {}
                      }
                    }
                  },
                  "noTariff": {
                    "summary": "Company has no tariff",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Тариф повинен бути в наявності!",
                        "details": {}
                      }
                    }
                  },
                  "tariffEmployees": {
                    "summary": "Too many users for the tariff",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Неможливо змінити тариф: ви маєте більше користувачів ніж дозволяє цей тариф",
                        "details": {}
                      }
                    }
                  },
                  "tariffPhones": {
                    "summary": "Too many numbers for the tariff",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Неможливо змінити тариф: ви маєте більше телефонів ніж дозволяє цей тариф",
                        "details": {}
                      }
                    }
                  },
                  "noPriceForCurrency": {
                    "summary": "No price in company currency",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Prefix 44 has no price for your currency!",
                        "details": {}
                      }
                    }
                  },
                  "tariffNoPrice": {
                    "summary": "Tariff has no price",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "This tariff has no price for your currency!",
                        "details": {}
                      }
                    }
                  },
                  "renewNoPacket": {
                    "summary": "Renewal packet invalid",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Talk packet renewal requires a valid talk_package_id for this prefix",
                        "details": {}
                      }
                    }
                  },
                  "noPhonesAvailable": {
                    "summary": "Prefix has no free numbers",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Trying to buy prefix that has no phones available!",
                        "details": {}
                      }
                    }
                  },
                  "esimMissing": {
                    "summary": "eSIM bundle not found",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "This esim bundle doesn`t exist!",
                        "details": {}
                      }
                    }
                  },
                  "esimNoPrice": {
                    "summary": "eSIM bundle has no price",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "This esim bundle has no price for your currency!",
                        "details": {}
                      }
                    }
                  },
                  "promoMissing": {
                    "summary": "Promo code not found",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "This promo code isn`t exist!",
                        "details": {}
                      }
                    }
                  },
                  "promoDate": {
                    "summary": "Promo code expired",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Promo not available at this date!",
                        "details": {}
                      }
                    }
                  },
                  "promoLimit": {
                    "summary": "Promo code limit reached",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Use limit has been reached!",
                        "details": {}
                      }
                    }
                  },
                  "promoUsed": {
                    "summary": "Promo code already used",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "You already you this promo code!",
                        "details": {}
                      }
                    }
                  },
                  "promoCompany": {
                    "summary": "Promo code for another company",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "This promo is not available for this company!",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/orders\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"payment_system\": 0,\n    \"phones\": [\n      {\n        \"prefix_id\": 9,\n        \"quantity\": 1\n      }\n    ],\n    \"auto_renewal\": true\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/orders\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"payment_system\": 0,\n      \"phones\": [\n        {\n          \"prefix_id\": 9,\n          \"quantity\": 1\n        }\n      ],\n      \"auto_renewal\": true\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/orders\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"payment_system\":0,\"phones\":[{\"prefix_id\":9,\"quantity\":1}],\"auto_renewal\":true}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Order by id",
        "description": "Returns one order by its numeric `id` (not `orderId`). Poll it after `POST /orders` until `orderStatus`\nbecomes `approved` or `declined`; on decline see `orderJSON.payment.message`. Use `populate` for\n`payment_system`, `currency`, `phones`, `payments`. Request only orders of your own company.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer",
              "example": 42
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/OrdersOrder"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 42,
                    "attributes": {
                      "orderId": "1788231620358546",
                      "orderStatus": "declined",
                      "orderType": "outgoing",
                      "amount": 6.49,
                      "auto_renewal": true,
                      "redirect_link": "global",
                      "source": "default",
                      "template_name": null,
                      "additional_data": null,
                      "orderJSON": {
                        "total": 6.49,
                        "goodsTotal": 5.99,
                        "starterFee": 0.5,
                        "refill": 0.5,
                        "tariff": null,
                        "esim": null,
                        "promo": null,
                        "domain": [],
                        "recall_widget": [],
                        "callback_widget": [],
                        "sip-trunk": [],
                        "incomingOrderId": null,
                        "phones": [
                          {
                            "prefix_id": 9,
                            "prefix_number": "44",
                            "quantity": 1,
                            "setup_price": 5.99,
                            "attached_phones": [
                              {
                                "id": 25,
                                "service": "default",
                                "fullnumber": "380441234567"
                              }
                            ]
                          }
                        ],
                        "payment": {
                          "processUrl": "https://api.onevoiplanet.com/api/orders/process-payment/1788231620358546",
                          "message": "Not enough money on balance"
                        }
                      },
                      "createdAt": "2026-09-01T03:00:20.402Z",
                      "updatedAt": "2026-09-01T03:00:25.402Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/orders/42\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/orders/42\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/orders/42\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/orders/calculate-order-price": {
      "post": {
        "operationId": "calculateOrderPrice",
        "tags": [
          "Orders"
        ],
        "summary": "Calculate order price (no charge)",
        "description": "Calculates the same `orderJSON` as `POST /orders` would produce — prices of numbers, packets, tariff, eSIM,\nextras, starter fee and total — **without creating an order or charging anything**. Accepts the same fields;\nthe top-up amount is passed as `amount` here. The company and currency are taken from the token.\n`goodsTotal` is the price of goods only (compare with the balance to decide whether paying from the balance\nis possible), `total` includes `refill` (top-up + starter fee). Rate limit: 30 requests per minute.\n\n\n**Required role:** Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "$ref": "#/components/schemas/OrdersCalculateInput"
                  }
                }
              },
              "example": {
                "data": {
                  "phones": [
                    {
                      "prefix_id": 9,
                      "quantity": 1,
                      "talk_package_id": 2
                    }
                  ],
                  "amount": 0
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculated order snapshot (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrdersOrderJSON"
                },
                "example": {
                  "total": 10.49,
                  "goodsTotal": 9.99,
                  "starterFee": 0.5,
                  "refill": 0.5,
                  "tariff": null,
                  "esim": null,
                  "promo": null,
                  "domain": [],
                  "recall_widget": [],
                  "callback_widget": [],
                  "sip-trunk": [],
                  "incomingOrderId": null,
                  "user_ip": null,
                  "payment": null,
                  "phones": [
                    {
                      "prefix_id": 9,
                      "prefix_number": "44",
                      "quantity": 1,
                      "talk_package_id": 2,
                      "setup_price": 5.99,
                      "talk_package_price": 4
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error (same messages as `POST /orders`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Prefix 44 has no price for your currency!",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/orders/calculate-order-price\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"phones\": [\n      {\n        \"prefix_id\": 9,\n        \"quantity\": 1,\n        \"talk_package_id\": 2\n      }\n    ],\n    \"amount\": 0\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/orders/calculate-order-price\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"phones\": [\n        {\n          \"prefix_id\": 9,\n          \"quantity\": 1,\n          \"talk_package_id\": 2\n        }\n      ],\n      \"amount\": 0\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/orders/calculate-order-price\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"phones\":[{\"prefix_id\":9,\"quantity\":1,\"talk_package_id\":2}],\"amount\":0}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip/my": {
      "get": {
        "operationId": "getMySip",
        "tags": [
          "SIP"
        ],
        "summary": "My SIP line",
        "description": "Returns the SIP line (internal extension) of the authenticated employee together with the numbers that ring on it\n(`inPhones`), the outgoing line (`outPhone`), the SIP domain and the call groups the employee belongs to.\n\nUse it to configure a softphone or IP phone:\n- **Username / Auth user:** `<number>@<domain.name>.sip.onevoiplanet.com` (e.g. `001@example.sip.onevoiplanet.com`)\n- **Password:** the `password` field (returned by this method only, treat it as a secret)\n- **SIP server / registrar / realm:** `<domain.name>.sip.onevoiplanet.com`, port 5060 UDP\n- **WebRTC (browser):** `wss://<domain.name>.sip.onevoiplanet.com:443`\n\nThe response is a flat object (no Strapi `data/attributes` envelope). Returns an empty body if the employee has no SIP line.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "SIP line of the current user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SipMyLine"
                },
                "example": {
                  "id": 12,
                  "number": "001",
                  "createdAt": "2026-01-15T10:00:00.000Z",
                  "updatedAt": "2026-02-01T09:30:00.000Z",
                  "password": "********",
                  "record_call": true,
                  "call_forwarding": "off",
                  "redirectActivity": false,
                  "redirectTo": null,
                  "user": {
                    "id": 7,
                    "group_employee": {
                      "id": 3,
                      "group": {
                        "id": 2,
                        "name": "Sales",
                        "in_phones": [],
                        "out_phone": null
                      }
                    }
                  },
                  "inPhones": [
                    {
                      "id": 25,
                      "number": "1234567",
                      "fullnumber": "380441234567",
                      "channels": 4,
                      "type": "simple",
                      "reserved": false,
                      "priority": 0,
                      "phone_prefix": {
                        "id": 9,
                        "prefix": "44"
                      },
                      "country_prefix": {
                        "id": 1,
                        "country": "UA",
                        "prefix": "380",
                        "iso": "UA"
                      }
                    }
                  ],
                  "outPhone": null,
                  "domain": {
                    "id": 5,
                    "name": "example",
                    "createdAt": "2026-01-15T10:00:00.000Z",
                    "updatedAt": "2026-01-15T10:00:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sip/my\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip/my\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip/my\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip/my/out-lines": {
      "get": {
        "operationId": "getMySipOutLines",
        "tags": [
          "SIP"
        ],
        "summary": "My outgoing lines",
        "description": "Returns the company numbers the current employee may use as caller ID for outgoing calls: numbers attached to his SIP line\nas outgoing lines plus the outgoing lines of his call groups. Only numbers with an active (paid) subscription are returned.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of outgoing numbers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Phone numbers.",
                      "items": {
                        "$ref": "#/components/schemas/SipPhoneLine"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 25,
                      "attributes": {
                        "number": "1234567",
                        "fullnumber": "380441234567",
                        "channels": 4,
                        "type": "simple",
                        "reserved": false,
                        "priority": 0,
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-02-01T09:30:00.000Z",
                        "country_prefix": {
                          "id": 1,
                          "country": "UA",
                          "prefix": "380",
                          "iso": "UA"
                        },
                        "phone_prefix": {
                          "id": 9,
                          "prefix": "44"
                        }
                      }
                    }
                  ],
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sip/my/out-lines\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip/my/out-lines\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip/my/out-lines\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sips/{id}": {
      "get": {
        "operationId": "getSip",
        "tags": [
          "SIP"
        ],
        "summary": "Get SIP line",
        "description": "Returns one SIP line of the company by id. Only lines whose owner belongs to your company are visible\n(`data: null` otherwise). Useful `populate`: `domain`, `user`, `inPhones`, `outPhone`, `out_phone_lines`.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "SIP line id.",
            "schema": {
              "type": "integer",
              "example": 12
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "SIP line.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipLine"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 12,
                    "attributes": {
                      "number": "001",
                      "password": "********",
                      "record_call": true,
                      "call_forwarding": "off",
                      "redirectActivity": false,
                      "redirectTo": null,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T09:30:00.000Z",
                      "domain": {
                        "data": {
                          "id": 5,
                          "attributes": {
                            "name": "example"
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sips/12\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sips/12\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sips/12\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateSip",
        "tags": [
          "SIP"
        ],
        "summary": "Update SIP line",
        "description": "Updates the settings of an employee's SIP line. Only these fields are accepted (others are silently ignored):\n`domain`, `record_call`, `call_forwarding`, `redirectActivity`, `redirectTo`.\nStrapi envelope `{ \"data\": {...} }` is required.\n\n`redirectActivity: true` together with `redirectTo` forwards every call to this extension to an external number.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Re-provisions the SIP account on the registrar (background job); changing the domain changes the SIP login. Creates/removes the call-redirect rule on the PBX when redirectActivity/redirectTo change.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "SIP line id.",
            "schema": {
              "type": "integer",
              "example": 12
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Fields to update (Strapi envelope).",
                    "properties": {
                      "domain": {
                        "type": "integer",
                        "description": "Id of the company SIP domain to move the line to.",
                        "example": 5
                      },
                      "record_call": {
                        "type": "boolean",
                        "description": "Record calls of this line.",
                        "example": true
                      },
                      "call_forwarding": {
                        "type": "string",
                        "enum": [
                          "off",
                          "on",
                          "no_answer"
                        ],
                        "description": "Call forwarding mode: off, on (always), no_answer.",
                        "example": "off"
                      },
                      "redirectActivity": {
                        "type": "boolean",
                        "description": "Enable redirect of all calls to `redirectTo`.",
                        "example": true
                      },
                      "redirectTo": {
                        "type": "string",
                        "minLength": 5,
                        "description": "External number for redirect (digits, min 5).",
                        "example": "380441234567"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "record_call": true,
                  "call_forwarding": "no_answer",
                  "redirectActivity": true,
                  "redirectTo": "380441234567"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated SIP line.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipLine"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 12,
                    "attributes": {
                      "number": "001",
                      "password": "********",
                      "record_call": true,
                      "call_forwarding": "no_answer",
                      "redirectActivity": true,
                      "redirectTo": "380441234567",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T09:30:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error: `Empty Data` (no `data`), `Invalid call_forwarding`, `Invalid sip` (id not found).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Invalid call_forwarding",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/sips/12\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"record_call\": true,\n    \"call_forwarding\": \"no_answer\",\n    \"redirectActivity\": true,\n    \"redirectTo\": \"380441234567\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sips/12\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"record_call\": true,\n      \"call_forwarding\": \"no_answer\",\n      \"redirectActivity\": true,\n      \"redirectTo\": \"380441234567\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sips/12\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"record_call\":true,\"call_forwarding\":\"no_answer\",\"redirectActivity\":true,\"redirectTo\":\"380441234567\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sips/changePassword/{id}": {
      "post": {
        "operationId": "changeSipPassword",
        "tags": [
          "SIP"
        ],
        "summary": "Change SIP password",
        "description": "Sets a new SIP password for an employee's line. The password is supplied by the caller (it is not generated by the server)\nand must be repeated in `confirmPassword`. After the change every softphone/IP phone using the old password loses registration\nand must be reconfigured. Returns the updated line as a flat object.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Pushes the new password to the SIP registrar (background job); existing registrations with the old password stop working.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "SIP line id.",
            "schema": {
              "type": "integer",
              "example": 12
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "password",
                      "confirmPassword"
                    ],
                    "description": "New password.",
                    "properties": {
                      "password": {
                        "type": "string",
                        "description": "New SIP password.",
                        "example": "N3wStr0ngPass"
                      },
                      "confirmPassword": {
                        "type": "string",
                        "description": "Must be equal to `password`.",
                        "example": "N3wStr0ngPass"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "password": "N3wStr0ngPass",
                  "confirmPassword": "N3wStr0ngPass"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated SIP line (flat object).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SipLineFlat"
                },
                "example": {
                  "id": 12,
                  "number": "001",
                  "password": "********",
                  "record_call": true,
                  "call_forwarding": "off",
                  "redirectActivity": false,
                  "redirectTo": null,
                  "createdAt": "2026-01-15T10:00:00.000Z",
                  "updatedAt": "2026-02-01T09:30:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`Invalid data` (missing password/confirmPassword), `Confirm password doesn`t match with password`, `Invalid sip` (id not found).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Confirm password doesn`t match with password",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/sips/changePassword/12\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"password\": \"N3wStr0ngPass\",\n    \"confirmPassword\": \"N3wStr0ngPass\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sips/changePassword/12\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"password\": \"N3wStr0ngPass\",\n      \"confirmPassword\": \"N3wStr0ngPass\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sips/changePassword/12\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"password\":\"N3wStr0ngPass\",\"confirmPassword\":\"N3wStr0ngPass\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks": {
      "get": {
        "operationId": "listSipTrunks",
        "tags": [
          "SIP"
        ],
        "summary": "List SIP trunks",
        "description": "Returns the SIP trunks (connections to external carriers / your own PBX) of the company with their external numbers.\n`registration` is computed live from the SIP proxy: `true` when at least one number of the trunk is currently registered at the carrier.\nStandard Strapi filters, sorting and pagination apply; the result is always limited to your company.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "List of trunks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Trunks.",
                      "items": {
                        "$ref": "#/components/schemas/SipTrunk"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 3,
                      "attributes": {
                        "name": "Carrier trunk",
                        "registration": true,
                        "connection_credentials": [
                          {
                            "id": 1,
                            "__component": "trunk-connection-type.by-credentials",
                            "login": "user123",
                            "host": "sip.carrier.example",
                            "port": "5060"
                          }
                        ],
                        "phones": [
                          {
                            "id": 44,
                            "fullnumber": "380441234567"
                          }
                        ],
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-02-01T09:30:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sip-trunks\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createSipTrunk",
        "tags": [
          "SIP"
        ],
        "summary": "Create SIP trunk (paid)",
        "description": "Orders a new SIP trunk. A trunk is a paid service: the method does **not** create the trunk directly — it creates an order\nfor the tariff price `per_trunk_price` and answers **402 Payment Required** with the order in `error.details`.\nThe amount is charged from the company balance immediately; once the order is approved the trunk is created and a monthly\nsubscription for it is started. If the balance is insufficient the order is declined and no trunk is created.\nPoll `GET /sip-trunks` or the order to see the result.\n\nConnection types (dynamic zone `connection_credentials`):\n- `trunk-connection-type.by-credentials` — registration at the carrier with `login`/`password`/`host`/`port`;\n- `trunk-connection-type.by-ip` — IP authentication, `ip` of your PBX.\n\nRequires an active tariff that allows external trunks (`maxAllowedExternalTrunks`), an active company and a login without Cyrillic letters.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Creates an order and charges `per_trunk_price` from the company balance; creates the trunk and a monthly subscription after the order is approved.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "name"
                    ],
                    "description": "Trunk data (Strapi envelope).",
                    "properties": {
                      "name": {
                        "type": "string",
                        "minLength": 3,
                        "maxLength": 50,
                        "description": "Trunk name.",
                        "example": "Carrier trunk"
                      },
                      "registration": {
                        "type": "boolean",
                        "description": "Register the trunk numbers at the carrier (outbound registration). Default false.",
                        "example": true
                      },
                      "connection_credentials": {
                        "type": "array",
                        "description": "Connection settings (one component).",
                        "items": {
                          "$ref": "#/components/schemas/SipTrunkConnectionInput"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "name": "Carrier trunk",
                  "registration": true,
                  "connection_credentials": [
                    {
                      "__component": "trunk-connection-type.by-credentials",
                      "login": "user123",
                      "password": "s3cret",
                      "host": "sip.carrier.example",
                      "port": "5060"
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Trunk created directly (only when the tariff has no trunk price; in practice the method answers 402).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipTrunk"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 3,
                    "attributes": {
                      "name": "Carrier trunk",
                      "registration": true,
                      "connection_credentials": [
                        {
                          "id": 1,
                          "__component": "trunk-connection-type.by-credentials",
                          "login": "user123",
                          "host": "sip.carrier.example",
                          "port": "5060"
                        }
                      ],
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Order created, payment is being processed. `error.details` contains the created order (`orderId`, `orderStatus: new`, `amount`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 402,
                    "name": "PaymentRequiredError",
                    "message": "Processing payment",
                    "details": {
                      "id": 501,
                      "orderId": "ORD-2026-000501",
                      "orderStatus": "new",
                      "orderType": "outgoing",
                      "amount": 5
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "No permission, or policy failed: `To continue buy tariff!` (no tariff), `Policy Failed` (tariff forbids external trunks), `Setup `per_trunk_price` for this tariff, action not allowed!`, `Login can`t contain cyrillic symbols!`, `Company disabled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "PolicyError",
                    "message": "To continue buy tariff!",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/sip-trunks\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"name\": \"Carrier trunk\",\n    \"registration\": true,\n    \"connection_credentials\": [\n      {\n        \"__component\": \"trunk-connection-type.by-credentials\",\n        \"login\": \"user123\",\n        \"password\": \"s3cret\",\n        \"host\": \"sip.carrier.example\",\n        \"port\": \"5060\"\n      }\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"name\": \"Carrier trunk\",\n      \"registration\": true,\n      \"connection_credentials\": [\n        {\n          \"__component\": \"trunk-connection-type.by-credentials\",\n          \"login\": \"user123\",\n          \"password\": \"s3cret\",\n          \"host\": \"sip.carrier.example\",\n          \"port\": \"5060\"\n        }\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"name\":\"Carrier trunk\",\"registration\":true,\"connection_credentials\":[{\"__component\":\"trunk-connection-type.by-credentials\",\"login\":\"user123\",\"password\":\"s3cret\",\"host\":\"sip.carrier.example\",\"port\":\"5060\"}]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/{id}": {
      "get": {
        "operationId": "getSipTrunk",
        "tags": [
          "SIP"
        ],
        "summary": "Get SIP trunk",
        "description": "Returns one trunk of the company by id (`data: null` if it belongs to another company). Use `populate[phones]=true` to get its external numbers.\nNumeric and boolean query values are coerced (`\"true\"` → `true`).\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Trunk id.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Trunk.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipTrunk"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 3,
                    "attributes": {
                      "name": "Carrier trunk",
                      "registration": true,
                      "connection_credentials": [
                        {
                          "id": 1,
                          "__component": "trunk-connection-type.by-credentials",
                          "login": "user123",
                          "host": "sip.carrier.example",
                          "port": "5060"
                        }
                      ],
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T09:30:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sip-trunks/3\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/3\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/3\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateSipTrunk",
        "tags": [
          "SIP"
        ],
        "summary": "Update SIP trunk",
        "description": "Updates name, registration flag or connection settings of a trunk. Strapi envelope `{ \"data\": {...} }`.\nToggling `registration` enables/disables outbound registration of all trunk numbers at the carrier.\nReturns `data: null` if the trunk does not belong to your company.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Re-applies registration of the trunk numbers on the SIP proxy (register/unregister at the carrier).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Trunk id.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Fields to update.",
                    "properties": {
                      "name": {
                        "type": "string",
                        "minLength": 3,
                        "maxLength": 50,
                        "description": "Trunk name.",
                        "example": "Carrier trunk"
                      },
                      "registration": {
                        "type": "boolean",
                        "description": "Register trunk numbers at the carrier.",
                        "example": false
                      },
                      "connection_credentials": {
                        "type": "array",
                        "description": "Connection settings (replaces the existing component).",
                        "items": {
                          "$ref": "#/components/schemas/SipTrunkConnectionInput"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "registration": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated trunk.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipTrunk"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 3,
                    "attributes": {
                      "name": "Carrier trunk",
                      "registration": false,
                      "connection_credentials": [
                        {
                          "id": 1,
                          "__component": "trunk-connection-type.by-credentials",
                          "login": "user123",
                          "host": "sip.carrier.example",
                          "port": "5060"
                        }
                      ],
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T09:30:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission or `Company disabled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "ForbiddenError",
                    "message": "Company disabled",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/sip-trunks/3\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"registration\": false\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/3\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"registration\": false\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/3\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"registration\":false}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteSipTrunk",
        "tags": [
          "SIP"
        ],
        "summary": "Delete SIP trunk",
        "description": "Deletes a trunk of the company. All external numbers attached to the trunk are detached from SIP lines, groups and IVRs,\nunregistered at the carrier and **deleted** together with their subscriptions. The trunk subscription is revoked. Irreversible.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Irreversibly deletes the trunk and all its external numbers, revokes their subscriptions, removes registrations on the SIP proxy.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Trunk id.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted trunk.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipTrunk"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 3,
                    "attributes": {
                      "name": "Carrier trunk",
                      "registration": false,
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-02-01T09:30:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/sip-trunks/3\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/3\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/3\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sip-trunks/check-connection": {
      "post": {
        "operationId": "checkSipTrunkConnection",
        "tags": [
          "SIP"
        ],
        "summary": "Test trunk credentials",
        "description": "Performs a real SIP REGISTER (with `Expires: 2`) from the OneVOIPlanet servers to the carrier using the supplied credentials\nand waits ~1 second for the answer. Use it before creating a `by-credentials` trunk.\n`connected: true` means the carrier accepted the registration; otherwise `status` holds the SIP response (e.g. 401/403) or nulls on timeout.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Sends a SIP REGISTER request to the external carrier (short-lived registration, expires in 2 seconds).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "login",
                      "password",
                      "host",
                      "port"
                    ],
                    "description": "Carrier credentials.",
                    "properties": {
                      "login": {
                        "type": "string",
                        "description": "SIP username at the carrier (no Cyrillic).",
                        "example": "user123"
                      },
                      "password": {
                        "type": "string",
                        "description": "SIP password.",
                        "example": "s3cret"
                      },
                      "host": {
                        "type": "string",
                        "description": "Carrier SIP server host.",
                        "example": "sip.carrier.example"
                      },
                      "port": {
                        "type": "string",
                        "description": "SIP port.",
                        "example": "5060"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "login": "user123",
                  "password": "s3cret",
                  "host": "sip.carrier.example",
                  "port": "5060"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Registration result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Result envelope.",
                      "properties": {
                        "attributes": {
                          "type": "object",
                          "description": "Result.",
                          "properties": {
                            "connected": {
                              "type": "boolean",
                              "description": "True if REGISTER succeeded.",
                              "example": true
                            },
                            "status": {
                              "type": "object",
                              "description": "Last SIP response.",
                              "properties": {
                                "status_code": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ],
                                  "description": "SIP status code (null on timeout).",
                                  "example": 200
                                },
                                "reason_phrase": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "SIP reason phrase.",
                                  "example": "OK"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "attributes": {
                      "connected": true,
                      "status": {
                        "status_code": 200,
                        "reason_phrase": "OK"
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "SIP stack failed to start with these parameters (message from the SIP library).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Invalid parameter: uri",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "No permission or `Login can`t contain cyrillic symbols!`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 403,
                    "name": "PolicyError",
                    "message": "Login can`t contain cyrillic symbols!",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/sip-trunks/check-connection\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"login\": \"user123\",\n    \"password\": \"s3cret\",\n    \"host\": \"sip.carrier.example\",\n    \"port\": \"5060\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sip-trunks/check-connection\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"login\": \"user123\",\n      \"password\": \"s3cret\",\n      \"host\": \"sip.carrier.example\",\n      \"port\": \"5060\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sip-trunks/check-connection\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"login\":\"user123\",\"password\":\"s3cret\",\"host\":\"sip.carrier.example\",\"port\":\"5060\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/domains": {
      "get": {
        "operationId": "listDomains",
        "tags": [
          "SIP"
        ],
        "summary": "List SIP domains",
        "description": "Returns the SIP domains of the company. A domain is the realm part of SIP logins: `<number>@<name>.sip.onevoiplanet.com`.\nStandard filters/sort/pagination; always limited to your company. `populate[sips]`, `populate[groups]`, `populate[ivrs]` show what uses the domain.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "List of domains.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Domains.",
                      "items": {
                        "$ref": "#/components/schemas/SipDomain"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 5,
                      "attributes": {
                        "name": "example",
                        "createdAt": "2026-01-15T10:00:00.000Z",
                        "updatedAt": "2026-01-15T10:00:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/domains\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/domains\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/domains\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "post": {
        "operationId": "createDomain",
        "tags": [
          "SIP"
        ],
        "summary": "Create SIP domain",
        "description": "Creates a SIP domain. `name` is 1–10 Latin letters/digits/underscore, unique across the platform; keep it short — it becomes part of\nevery SIP login (`<number>@<name>.sip.onevoiplanet.com`).\n\nPricing: the tariff includes `maxDomains` free domains. When the company already has that many, the new domain is **paid**:\nthe balance is checked against `domain_cost` (see `GET /domains/cost`); if sufficient, the domain is created, an order is\ncreated and `domain_cost` is charged from the balance immediately, then a monthly subscription for the domain is started.\nIf the balance is insufficient the request fails with 400 `Not enough money on balance` and a declined order is recorded.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** When the free domain limit of the tariff is exhausted: creates an order, charges `domain_cost` from the balance and starts a monthly domain subscription. Within the limit: none.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "name"
                    ],
                    "description": "Domain data (Strapi envelope).",
                    "properties": {
                      "name": {
                        "type": "string",
                        "maxLength": 10,
                        "pattern": "^\\w*$",
                        "description": "Domain name (Latin letters, digits, `_`; max 10; unique).",
                        "example": "example"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "name": "example"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created domain.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipDomain"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 6,
                    "attributes": {
                      "name": "example",
                      "createdAt": "2026-02-01T09:30:00.000Z",
                      "updatedAt": "2026-02-01T09:30:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`Not enough money on balance` (paid domain, balance too low) or Strapi validation (`This attribute must be unique`, length/regex).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Not enough money on balance",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/domains\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"name\": \"example\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/domains\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"name\": \"example\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/domains\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"name\":\"example\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/domains/cost": {
      "get": {
        "operationId": "getDomainCost",
        "tags": [
          "SIP"
        ],
        "summary": "Price of an extra domain",
        "description": "Returns the price of one additional SIP domain (`domain_cost` from the company tariff, in the company currency) —\nthe amount charged by `POST /domains` once the free domain limit is used up. `price` may come back empty if the tariff has no domain price.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Domain price.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Envelope.",
                      "properties": {
                        "attributes": {
                          "type": "object",
                          "description": "Result.",
                          "properties": {
                            "price": {
                              "type": "number",
                              "description": "Price of one extra domain in the company currency.",
                              "example": 2
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "attributes": {
                      "price": 2
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/domains/cost\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/domains/cost\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/domains/cost\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/domains/{id}": {
      "get": {
        "operationId": "getDomain",
        "tags": [
          "SIP"
        ],
        "summary": "Get SIP domain",
        "description": "Returns one domain of the company by id (`data: null` if it belongs to another company). Supports `populate`.\n\n**Required role:** Employee, Company owner (approved), Supervisor, Finance\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Domain id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Domain.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipDomain"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 5,
                    "attributes": {
                      "name": "example",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/domains/5\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/domains/5\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/domains/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteDomain",
        "tags": [
          "SIP"
        ],
        "summary": "Delete SIP domain",
        "description": "Deletes a domain of the company. Refused (400) while any SIP line, call group, IVR or recall widget still uses the domain —\n`error.details` lists them (`userName`, `groupName`, `ivrName`, `recall_widgetName`). On success the domain subscription is revoked.\n\n\n**Required role:** Company owner (approved), Supervisor\n\n**Side effects:** Irreversibly deletes the domain and revokes its monthly subscription.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Domain id.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted domain.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SipDomain"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 5,
                    "attributes": {
                      "name": "example",
                      "createdAt": "2026-01-15T10:00:00.000Z",
                      "updatedAt": "2026-01-15T10:00:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Domain is in use.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Domain is attached to user or to group or to recall widget or to IVR!",
                    "details": {
                      "userName": [
                        "John  Doe"
                      ],
                      "groupName": [
                        "Sales"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "description": "Returned when the domain id does not exist in your company (the server fails before the not-found check).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 500,
                    "name": "InternalServerError",
                    "message": "Internal Server Error",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/domains/5\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/domains/5\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/domains/5\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sms/sms-chats": {
      "get": {
        "operationId": "listSmsChats",
        "tags": [
          "SMS"
        ],
        "summary": "List SMS chats",
        "description": "Returns the SMS chats of the company. A chat is a dialogue between one company number and one external number\n(`dialog_with`); it is created automatically when the first incoming or outgoing message appears.\nThe list is always restricted to the company of the authenticated user (the `company` filter is added on the server).\n\nUseful query options: `sort=last_message_time:desc`, `filters[seen][$eq]=false` (chats with unread messages),\n`filters[dialog_with][$eq]=380671234567`, `filters[receiver_phone_relation][id][$eq]=7` (chats on one company number),\n`populate[receiver_phone_relation][fields][0]=fullnumber`. Messages of a chat are better fetched via\n`GET /sms/sms-messages?filters[chat][id][$eq]={chatId}`. Default page size is 10.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of chats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Chats.",
                      "items": {
                        "$ref": "#/components/schemas/SmsChat"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 12,
                      "attributes": {
                        "dialog_with": "380671234567",
                        "last_message": "Thank you, see you tomorrow",
                        "last_message_time": "2026-03-10T09:15:00.000Z",
                        "last_message_receiver": "380671234567",
                        "last_message_type": "outgoing",
                        "seen": true,
                        "alpha_name": "John Doe",
                        "createdAt": "2026-03-01T08:00:00.000Z",
                        "updatedAt": "2026-03-10T09:15:01.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sms/sms-chats\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/sms-chats\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/sms-chats\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sms/sms-chats/{id}": {
      "get": {
        "operationId": "getSmsChat",
        "tags": [
          "SMS"
        ],
        "summary": "Get SMS chat",
        "description": "Returns one chat by id. Use `populate[messages]=*` to include the messages, or\n`populate[receiver_phone_relation][fields][0]=fullnumber` to see the company number of the dialogue.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Chat id.",
            "schema": {
              "type": "integer",
              "example": 12
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Chat.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SmsChat"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 12,
                    "attributes": {
                      "dialog_with": "380671234567",
                      "last_message": "Thank you, see you tomorrow",
                      "last_message_time": "2026-03-10T09:15:00.000Z",
                      "last_message_receiver": "380671234567",
                      "last_message_type": "outgoing",
                      "seen": true,
                      "alpha_name": "John Doe",
                      "createdAt": "2026-03-01T08:00:00.000Z",
                      "updatedAt": "2026-03-10T09:15:01.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sms/sms-chats/12\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/sms-chats/12\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/sms-chats/12\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "put": {
        "operationId": "updateSmsChat",
        "tags": [
          "SMS"
        ],
        "summary": "Update SMS chat",
        "description": "Updates chat fields (Strapi core update, body is `{ \"data\": {...} }`). In practice only two fields make sense to change:\n`seen` (mark the whole chat as read/unread) and `alpha_name` (display name of the external party).\nOther fields (`last_message*`, `dialog_with`) are maintained automatically by incoming/outgoing messages — do not change them.\nTo mark individual messages as read use `PUT /sms/update-messages-status`.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Chat id.",
            "schema": {
              "type": "integer",
              "example": 12
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "Fields to update (Strapi envelope).",
                    "properties": {
                      "seen": {
                        "type": "boolean",
                        "description": "Whether the chat is read.",
                        "example": true
                      },
                      "alpha_name": {
                        "type": "string",
                        "description": "Display name of the external party.",
                        "example": "John Doe"
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "seen": true,
                  "alpha_name": "John Doe"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated chat.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SmsChat"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 12,
                    "attributes": {
                      "dialog_with": "380671234567",
                      "last_message": "Thank you, see you tomorrow",
                      "last_message_time": "2026-03-10T09:15:00.000Z",
                      "last_message_receiver": "380671234567",
                      "last_message_type": "outgoing",
                      "seen": true,
                      "alpha_name": "John Doe",
                      "createdAt": "2026-03-01T08:00:00.000Z",
                      "updatedAt": "2026-03-10T09:20:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Missing `data` envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "Missing \"data\" payload in the request body",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/sms/sms-chats/12\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"seen\": true,\n    \"alpha_name\": \"John Doe\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/sms-chats/12\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"seen\": true,\n      \"alpha_name\": \"John Doe\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/sms-chats/12\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"seen\":true,\"alpha_name\":\"John Doe\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteSmsChat",
        "tags": [
          "SMS"
        ],
        "summary": "Delete SMS chat",
        "description": "Permanently deletes the chat. Messages of the chat are not deleted — they remain in `GET /sms/sms-messages`\nbut lose the link to the chat; the next message with the same number will create a new chat.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Deletes the chat permanently.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Chat id.",
            "schema": {
              "type": "integer",
              "example": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted chat.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SmsChat"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 12,
                    "attributes": {
                      "dialog_with": "380671234567",
                      "last_message": "Thank you, see you tomorrow",
                      "last_message_time": "2026-03-10T09:15:00.000Z",
                      "last_message_receiver": "380671234567",
                      "last_message_type": "outgoing",
                      "seen": true,
                      "alpha_name": "John Doe",
                      "createdAt": "2026-03-01T08:00:00.000Z",
                      "updatedAt": "2026-03-10T09:20:00.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/sms/sms-chats/12\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/sms-chats/12\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/sms-chats/12\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sms/sms-messages": {
      "get": {
        "operationId": "listSmsMessages",
        "tags": [
          "SMS"
        ],
        "summary": "List SMS messages",
        "description": "Returns SMS messages (incoming and outgoing) of the company. The list is always restricted to the company of the\nauthenticated user (the `company` filter is added on the server).\n\nUseful query options: `filters[chat][id][$eq]=12` (messages of one chat), `filters[type][$eq]=incoming`,\n`filters[status][$eq]=error`, `filters[seen][$eq]=false`, `filters[receive_time][$gte]=2026-03-01`,\n`sort=receive_time:desc`, `populate[chat][fields][0]=dialog_with`. Default page size is 10.\nDelivery status of outgoing messages changes asynchronously (`queued` → `success` / `error`), re-read the message to see it.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Messages.",
                      "items": {
                        "$ref": "#/components/schemas/SmsMessage"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 101,
                      "attributes": {
                        "sender": "380441234567",
                        "receiver": "380671234567",
                        "receive_time": "2026-03-10T09:15:00.000Z",
                        "type": "outgoing",
                        "status": "success",
                        "seen": true,
                        "message": "Thank you, see you tomorrow",
                        "createdAt": "2026-03-10T09:15:00.000Z",
                        "updatedAt": "2026-03-10T09:15:03.000Z"
                      }
                    },
                    {
                      "id": 100,
                      "attributes": {
                        "sender": "380671234567",
                        "receiver": "380441234567",
                        "receive_time": "2026-03-10T09:10:00.000Z",
                        "type": "incoming",
                        "status": "success",
                        "seen": false,
                        "message": "Hello, is the office open tomorrow?",
                        "createdAt": "2026-03-10T09:10:00.000Z",
                        "updatedAt": "2026-03-10T09:10:00.000Z"
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 1,
                      "total": 2
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sms/sms-messages\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/sms-messages\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/sms-messages\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sms/sms-messages/{id}": {
      "get": {
        "operationId": "getSmsMessage",
        "tags": [
          "SMS"
        ],
        "summary": "Get SMS message",
        "description": "Returns one message by id, e.g. to poll the delivery `status` of a message sent via `POST /sms/outgoing/send`.\n`populate[chat]=*` includes the chat, `populate[receiver_phone_relation][fields][0]=fullnumber` — the company number.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Message id.",
            "schema": {
              "type": "integer",
              "example": 101
            }
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SmsMessage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "sender": "380441234567",
                      "receiver": "380671234567",
                      "receive_time": "2026-03-10T09:15:00.000Z",
                      "type": "outgoing",
                      "status": "success",
                      "seen": true,
                      "message": "Thank you, see you tomorrow",
                      "createdAt": "2026-03-10T09:15:00.000Z",
                      "updatedAt": "2026-03-10T09:15:03.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sms/sms-messages/101\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/sms-messages/101\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/sms-messages/101\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteSmsMessage",
        "tags": [
          "SMS"
        ],
        "summary": "Delete SMS message",
        "description": "Permanently deletes one message from the history. Does not cancel sending (an already queued message is still delivered)\nand does not refund the charge. The `last_message*` fields of the chat are not recalculated.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Deletes the message permanently.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Message id.",
            "schema": {
              "type": "integer",
              "example": 101
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SmsMessage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "sender": "380441234567",
                      "receiver": "380671234567",
                      "receive_time": "2026-03-10T09:15:00.000Z",
                      "type": "outgoing",
                      "status": "success",
                      "seen": true,
                      "message": "Thank you, see you tomorrow",
                      "createdAt": "2026-03-10T09:15:00.000Z",
                      "updatedAt": "2026-03-10T09:15:03.000Z"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X DELETE \"https://api.onevoiplanet.com/api/sms/sms-messages/101\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/sms-messages/101\", {\n  method: \"DELETE\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/sms-messages/101\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"DELETE\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sms/phones": {
      "get": {
        "operationId": "listSmsPhones",
        "tags": [
          "SMS"
        ],
        "summary": "Company numbers that can send SMS",
        "description": "Returns the company numbers whose number range supports outgoing SMS (`phone_prefix.outgoing_sms = true`).\nUse the `id` of a number as the `phone` field in `POST /sms/outgoing/send`.\nFor every number the price list of its range is included: `price_by_field` items named `sms_outgoing_price` and\n`sms_incoming_price` are the per-segment prices (the currency of each price list is not returned; the one in your company\ncurrency applies). Note: if a destination-based route price exists (see `POST /sms/outgoing/send`), it takes precedence over\n`sms_outgoing_price`. Only `pagination[page]`/`pagination[pageSize]` are accepted, default page size is 10.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          }
        ],
        "responses": {
          "200": {
            "description": "Numbers with SMS support.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Numbers.",
                      "items": {
                        "$ref": "#/components/schemas/SmsPhone"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 7,
                      "attributes": {
                        "fullnumber": "380441234567",
                        "country_prefix": {
                          "data": {
                            "id": 3,
                            "attributes": {
                              "iso": "UA"
                            }
                          }
                        },
                        "phone_prefix": {
                          "data": {
                            "id": 15,
                            "attributes": {
                              "outgoing_sms": true,
                              "prices": {
                                "data": [
                                  {
                                    "id": 40,
                                    "attributes": {
                                      "price_by_field": [
                                        {
                                          "id": 901,
                                          "name": "sms_outgoing_price",
                                          "price": 0.05
                                        },
                                        {
                                          "id": 902,
                                          "name": "sms_incoming_price",
                                          "price": 0.01
                                        }
                                      ]
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 10,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/sms/phones\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/phones\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/phones\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sms/outgoing/send": {
      "post": {
        "operationId": "sendSms",
        "tags": [
          "SMS"
        ],
        "summary": "Send SMS",
        "description": "Sends an SMS from a company number (`phone` — id of a number from `GET /sms/phones`) to `destination`.\n\n**Destination number.** Digits only are kept. The number is first interpreted as a national number of the sender's\ncountry (as dialled in a softphone), and only if that fails — as an international number without `+`.\nExample: from a Ukrainian number `0671234567` and `380671234567` both mean +380 67 123 4567. SMS to the United States are rejected.\n\n**Charging.** The charge is taken from the company balance immediately when the message is accepted, before delivery:\n`segments × price`, where `segments = ceil(message.length / 160)` (plain character count) and `price` is, in order of precedence:\nthe SMS route price for the destination country in the company currency (`price_local` if the destination is in the same\ncountry as the sender, otherwise `price_intl`), or, if no route exists, `sms_outgoing_price` of the sender's number range.\nThe debit appears in the payment history with the comment `Outgoing sms sms`. The charge is **not refunded** if the\nprovider later reports a delivery error.\n\n**Result.** The method returns the created message with `status: \"queued\"`; delivery goes through DIDWW asynchronously and the\nstatus becomes `success` or `error` — poll `GET /sms/sms-messages/{id}` (a push notification `smsStatus` is also sent).\nIf the balance (plus credit limit, if enabled) is lower than the charge, or no price is configured for the company currency,\nthe method still returns **200** with a message in `status: \"error\"`; nothing is sent and nothing is charged.\nThe chat with the destination number is created or updated automatically.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Sends an SMS via the provider (DIDWW) and immediately debits the company balance: ceil(length/160) segments × per-segment price (SMS route price for the destination country, otherwise sms_outgoing_price of the sender's number range), payment comment 'Outgoing sms sms'; no refund on delivery failure. Creates/updates the SMS chat and sends push notifications on status changes.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsSendRequest"
              },
              "example": {
                "data": {
                  "phone": 7,
                  "destination": "380671234567",
                  "message": "Thank you, see you tomorrow"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created outgoing message. `status: queued` — accepted and charged; `status: error` — not sent (insufficient balance or no price configured), not charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SmsSentMessage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 101,
                    "attributes": {
                      "sender": "380441234567",
                      "receiver": "380671234567",
                      "receive_time": "2026-03-10T09:15:00.000Z",
                      "type": "outgoing",
                      "status": "queued",
                      "seen": true,
                      "message": "Thank you, see you tomorrow",
                      "createdAt": "2026-03-10T09:15:00.000Z",
                      "updatedAt": "2026-03-10T09:15:00.000Z",
                      "company": {
                        "data": {
                          "id": 1,
                          "attributes": {
                            "currency": {
                              "data": {
                                "id": 1,
                                "attributes": {}
                              }
                            }
                          }
                        }
                      }
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Messages: `data.destination is a required field`, `data.phone is a required field`, `data.message is a required field`, `data.destination must be at least 3 characters`, `Incorrect phone provided` (the number does not belong to your company), `Phone not allowed to send sms` (the range has no outgoing SMS), `Incorrect destination number`, `SMS to the United States are not supported`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "phone": {
                    "summary": "Incorrect phone provided",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Incorrect phone provided",
                        "details": {}
                      }
                    }
                  },
                  "destination": {
                    "summary": "Incorrect destination number",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "Incorrect destination number",
                        "details": {}
                      }
                    }
                  },
                  "usa": {
                    "summary": "SMS to the United States are not supported",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "SMS to the United States are not supported",
                        "details": {}
                      }
                    }
                  },
                  "schema": {
                    "summary": "Missing field",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "data.message is a required field",
                        "details": {
                          "errors": [
                            {
                              "path": [
                                "data",
                                "message"
                              ],
                              "message": "data.message is a required field",
                              "name": "ValidationError"
                            }
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/sms/outgoing/send\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"phone\": 7,\n    \"destination\": \"380671234567\",\n    \"message\": \"Thank you, see you tomorrow\"\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/outgoing/send\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"phone\": 7,\n      \"destination\": \"380671234567\",\n      \"message\": \"Thank you, see you tomorrow\"\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/outgoing/send\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"phone\":7,\"destination\":\"380671234567\",\"message\":\"Thank you, see you tomorrow\"}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/sms/update-messages-status": {
      "put": {
        "operationId": "markSmsMessagesSeen",
        "tags": [
          "SMS"
        ],
        "summary": "Mark messages as read",
        "description": "Marks the listed messages of one chat as read (`seen = true`) and marks the chat itself as read.\nOnly messages that belong to the given `chat` are affected; if none of the `ids` belong to the chat, nothing changes and\n`data` is an empty array. The operation is idempotent. The reverse (mark as unread) is not supported.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsMarkSeenRequest"
              },
              "example": {
                "data": {
                  "chat": 12,
                  "ids": [
                    100,
                    101
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated messages with the state of their chat.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Updated messages (empty if no message matched).",
                      "items": {
                        "$ref": "#/components/schemas/SmsMessageSeenState"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 100,
                      "attributes": {
                        "seen": true,
                        "chat": {
                          "data": {
                            "id": 12,
                            "attributes": {
                              "seen": true
                            }
                          }
                        }
                      }
                    },
                    {
                      "id": 101,
                      "attributes": {
                        "seen": true,
                        "chat": {
                          "data": {
                            "id": 12,
                            "attributes": {
                              "seen": true
                            }
                          }
                        }
                      }
                    }
                  ],
                  "meta": {}
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/sms/update-messages-status\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"chat\": 12,\n    \"ids\": [\n      100,\n      101\n    ]\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/sms/update-messages-status\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"chat\": 12,\n      \"ids\": [\n        100,\n        101\n      ]\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/sms/update-messages-status\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"chat\":12,\"ids\":[100,101]}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/subscriptions": {
      "get": {
        "operationId": "listSubscriptions",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Subscriptions of the company",
        "description": "Strapi core list of the company's subscriptions (the company filter is added automatically). A subscription\nis created for every paid item (number, tariff, domain, widget, SIP trunk, external number) when an order is\napproved. Lifecycle: every night at 03:00 (server time, UTC) subscriptions whose `next_billing_date` has come\nare charged: `amount` is debited from the balance; if the balance is insufficient and a card is saved, the card\nis charged. On success `payeed` becomes `true` and `next_billing_date` moves one month ahead. On failure\n`payeed` stays `false`: the number stops working but can be restored within 30 days\n(`POST /subscriptions/{id}/restore`); after 30 days the number is released and cannot be restored.\n`active` is the auto-renewal switch (`PUT /subscriptions/update_state/{subscriptionId}`); inactive subscriptions\nare not charged and expire at `next_billing_date`.\nUseful: `populate[service_name][populate]=*` (what the subscription is for), `populate[payments]=true`,\n`filters[active][$eq]=true`, `filters[payeed][$eq]=false`, `sort=next_billing_date:asc`.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Filters"
          },
          {
            "$ref": "#/components/parameters/Populate"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Subscriptions.",
                      "items": {
                        "$ref": "#/components/schemas/Subscription"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 12,
                      "attributes": {
                        "next_billing_date": "2026-11-01",
                        "amount": 5.99,
                        "payeed": true,
                        "active": true,
                        "createdAt": "2026-04-22T07:08:01.714Z",
                        "updatedAt": "2026-10-01T03:00:04.393Z",
                        "service_name": [
                          {
                            "id": 385,
                            "__component": "subscription-service.phones",
                            "phones": {
                              "data": {
                                "id": 25,
                                "attributes": {
                                  "fullnumber": "380441234567"
                                }
                              }
                            }
                          }
                        ],
                        "payments": {
                          "data": [
                            {
                              "id": 101,
                              "attributes": {
                                "status": "approved",
                                "amount": -5.99,
                                "commentary": "Payment: 191703",
                                "createdAt": "2026-10-01T03:00:04.488Z",
                                "updatedAt": "2026-10-01T03:00:04.911Z"
                              }
                            }
                          ]
                        },
                        "payment_system": {
                          "data": null
                        }
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 25,
                      "pageCount": 1,
                      "total": 9
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/subscriptions\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/subscriptions\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/subscriptions\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/subscriptions/update_state/{subscriptionId}": {
      "put": {
        "operationId": "setSubscriptionAutoRenewal",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Switch auto-renewal",
        "description": "Enables or disables auto-renewal (`active`) of a paid subscription of your company. Only subscriptions with\n`payeed: true` can be switched; for an unpaid one restore it first. With `active: false` the subscription is\nnot charged at night and the service expires at `next_billing_date`. If the subscription is not found, belongs\nto another company or is not paid, `data` is `null`.\n\n\n**Required role:** Company owner (approved)\n\n**Side effects:** Changes the auto-renewal flag; sends an `autorenew_enabled`/`autorenew_cancelled` notification to the owner (Telegram/webhook).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "required": true,
            "description": "Subscription id.",
            "schema": {
              "type": "integer",
              "example": 12
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "required": [
                      "active"
                    ],
                    "description": "Payload.",
                    "properties": {
                      "active": {
                        "type": "boolean",
                        "description": "true — auto-renew monthly; false — stop charging, let the service expire.",
                        "example": false
                      }
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "active": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated state, or `data: null` if nothing was updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Updated subscription.",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "description": "Subscription id.",
                          "example": 12
                        },
                        "attributes": {
                          "type": "object",
                          "description": "Attributes.",
                          "properties": {
                            "active": {
                              "type": "boolean",
                              "description": "New auto-renewal state.",
                              "example": false
                            },
                            "next_billing_date": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date",
                              "description": "Next billing date.",
                              "example": "2026-11-01"
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 12,
                    "attributes": {
                      "active": false,
                      "next_billing_date": "2026-11-01"
                    }
                  },
                  "meta": {}
                }
              }
            }
          },
          "400": {
            "description": "`active` is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 400,
                    "name": "ValidationError",
                    "message": "`active` field must be provided",
                    "details": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X PUT \"https://api.onevoiplanet.com/api/subscriptions/update_state/12\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"data\": {\n    \"active\": false\n  }\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/subscriptions/update_state/12\", {\n  method: \"PUT\",\n  headers: { \"Authorization\": \"Bearer \" + token, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"data\": {\n      \"active\": false\n    }\n  }),\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/subscriptions/update_state/12\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"PUT\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token, \"Content-Type: application/json\"]);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode({\"data\":{\"active\":false}}));\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/subscriptions/{id}/restore": {
      "post": {
        "operationId": "restoreSubscription",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Restore an expired subscription (pay from balance)",
        "description": "Pays an overdue subscription of a number **from the company balance** and thereby restores the number.\nPossible within 30 days after `next_billing_date` (`recovery_status: restorable` in `GET /phones/subscriptions-list`).\nThe charged amount is the subscription `amount` or, if it is 0, the monthly price of the number prefix.\nNo card fallback: if the balance is insufficient the response is `{ ok: false, reason: \"insufficient_balance\" }` —\ntop up first (`POST /orders` with `addMoney`). The charge is asynchronous: the response is `status: \"processing\"`;\nwithin a few seconds `payeed` becomes `true` and `next_billing_date` moves forward (check `GET /subscriptions`).\nAuto-renewal (`active`) is **not** switched on — enable it separately. A repeated call within 2 minutes while\na charge is in flight does not charge twice.\n\n\n**Required role:** Company owner (approved), Company owner (registered, not yet approved)\n\n**Side effects:** Charges the subscription amount (or the number's monthly price) from the company balance; on success marks the subscription paid, moves `next_billing_date` one month ahead and re-activates the number.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Subscription id (see `subscription_id` in `GET /phones/subscriptions-list`).",
            "schema": {
              "type": "integer",
              "example": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result. `ok: true` — charge started; `ok: false` — see `reason`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionRestoreResult"
                },
                "examples": {
                  "processing": {
                    "summary": "Charge started",
                    "value": {
                      "ok": true,
                      "status": "processing",
                      "subscriptionId": 12,
                      "amount": 5.99
                    }
                  },
                  "alreadyActive": {
                    "summary": "Nothing to restore",
                    "value": {
                      "ok": false,
                      "reason": "already_active"
                    }
                  },
                  "nonRestorable": {
                    "summary": "More than 30 days overdue",
                    "value": {
                      "ok": false,
                      "reason": "non_restorable"
                    }
                  },
                  "insufficient": {
                    "summary": "Not enough balance",
                    "value": {
                      "ok": false,
                      "reason": "insufficient_balance",
                      "amount": 5.99,
                      "balance": 1.2
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or the price cannot be resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "idRequired": {
                    "summary": "Invalid id",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ValidationError",
                        "message": "subscription id required",
                        "details": {}
                      }
                    }
                  },
                  "noPrice": {
                    "summary": "No price",
                    "value": {
                      "data": null,
                      "error": {
                        "status": 400,
                        "name": "ApplicationError",
                        "message": "Cannot resolve subscription price",
                        "details": {}
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Subscription not found or belongs to another company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "data": null,
                  "error": {
                    "status": 404,
                    "name": "NotFoundError",
                    "message": "Subscription not found",
                    "details": {}
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X POST \"https://api.onevoiplanet.com/api/subscriptions/12/restore\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/subscriptions/12/restore\", {\n  method: \"POST\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/subscriptions/12/restore\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"POST\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    },
    "/phones/subscriptions-list": {
      "get": {
        "operationId": "listPhoneSubscriptions",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Numbers with subscription status",
        "description": "One row per company number joined with its subscription: paid state, next billing date, price, catalogue data\n(prefix, owner, country), lifecycle status (`active` / `expired`) and whether an expired number can still be\nrestored (`recovery_status`: `restorable` — within 30 days after the billing date, `non_restorable` — later,\n`not_applicable` — active, `unknown` — no billing anchor). Numbers already detached from the company but still\nreferenced by its old subscriptions are included with `is_detached: true` (history). Also contains the\nregulator verification status of the number. Designed for the \"My numbers\" screen; employees may call it.\n\n\n**Required role:** Employee, Company owner (approved), Supervisor\n\n**Side effects:** None",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, from 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Rows per page (1–100, default 20).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "example": 20
            }
          },
          {
            "name": "tab",
            "in": "query",
            "required": false,
            "description": "Filter by status: `all`, `active`, `expired`.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "active",
                "expired"
              ],
              "default": "all",
              "example": "all"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "required": false,
            "description": "Sort field: `fullnumber`, `next_billing_date`, `created_at`, `status`, `recovery_status`.",
            "schema": {
              "type": "string",
              "enum": [
                "fullnumber",
                "next_billing_date",
                "created_at",
                "status",
                "recovery_status"
              ],
              "default": "fullnumber",
              "example": "fullnumber"
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "required": false,
            "description": "`asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc",
              "example": "asc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rows (note: items have `attributes` only, without `id`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Rows.",
                      "items": {
                        "type": "object",
                        "description": "Row envelope.",
                        "properties": {
                          "attributes": {
                            "$ref": "#/components/schemas/SubscriptionPhoneRow"
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "attributes": {
                        "phone_id": 25,
                        "fullnumber": "380441234567",
                        "number": "1234567",
                        "custom": null,
                        "verification_required": null,
                        "verified_at": null,
                        "created_at": "2026-04-07T12:25:18.282Z",
                        "updated_at": "2026-10-01T03:00:04.393Z",
                        "company_id": 7,
                        "subscription_id": 12,
                        "active": true,
                        "payeed": true,
                        "next_billing_date": "2026-11-01",
                        "subscription_amount": 5.99,
                        "prefix_id": 9,
                        "prefix": "44",
                        "owner_name": "Kyiv",
                        "owner_type": "city",
                        "country_iso": "UA",
                        "prefix_price": 5.99,
                        "status": "active",
                        "recovery_status": "not_applicable",
                        "verification": {
                          "required": false,
                          "status": "none",
                          "verification_id": null,
                          "reject_reasons": null,
                          "reject_comment": null,
                          "verified_at": null
                        },
                        "lifecycle_anchor_date": "2026-11-01",
                        "lifecycle_days_after_anchor": -30,
                        "is_detached": false
                      }
                    }
                  ],
                  "meta": {
                    "pagination": {
                      "page": 1,
                      "pageSize": 20,
                      "pageCount": 1,
                      "total": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -X GET \"https://api.onevoiplanet.com/api/phones/subscriptions-list\" \\\n  -H \"Authorization: Bearer $TOKEN\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.onevoiplanet.com/api/phones/subscriptions-list\", {\n  method: \"GET\",\n  headers: { \"Authorization\": \"Bearer \" + token, },\n});\nconst json = await res.json();\nconsole.log(res.status, json);"
          },
          {
            "lang": "PHP",
            "label": "PHP (curl)",
            "source": "<?php\n$ch = curl_init(\"https://api.onevoiplanet.com/api/phones/subscriptions-list\");\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"GET\");\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\"Authorization: Bearer \" . $token]);\n$response = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n$data = json_decode($response, true);"
          }
        ]
      }
    }
  }
}